Module 3: Analysing results produce from Modules 1 and 2

library(png)

Organize data

# A frequent scenario in our analysis code is that we need to read thousands of 
# output files into a single table. 
# There are a couple flavors of file name that need to be dealt with and the 
# output file needs to include a column with the full file name for reference 
# later. We want to save the final table as it's own file to a folder outside of 
# the one we just read. 

## Here is one flaver where the input is two folders, one with extant 
# simulations and one with extinct simulations. Need to read the files and 
# produce two tables, one for extinct and one for extant. 

## First consolidate the available files into a single table
concatenate <- function(path) {
 available <- list.files(path, full.names = TRUE)
 name <- unlist(strsplit(available[1], split="_"))
 n <- length(available)
 n_name <- length(name) - 1
 load(available[1])
 ncol_files <- length(Sim_statistics[[1]]) + n_name
 files <- matrix(nrow = n,
                 ncol = ncol_files)
 split_names <- function(x){unlist(strsplit(x, split = "_"))}
 name_available <- do.call(rbind, lapply(as.list(available), split_names))
 files[, 1:n_name] <- name_available[, -ncol(name_available)]

 for (i in 1:n) {
   error <- try(load(available[i]), silent = TRUE)
   if (class(error) != "try-error") {
     files[i, (n_name + 1):ncol_files] <- Sim_statistics[[1]]
   }
 }


 files <- files[, -c(1, 2, 3, 5, 7, 8, 13, 18, 23, 28, 33, 35)]
 last_name <- colnames(Sim_statistics[[1]])
 if (is.null(last_name)) {

   files <- files[, -ncol(files)]
 }
 colnames(files) <-  c("Sim_stats_rep", "combo", paste0("P.speciation", 1:4),
                       paste0("P.extinct", 1:4),  paste0("P.diffus", 1:4),
                       paste0("P.TO", 1:4),  paste0("P.Arisal", 1:4), 
                       "timesteps", "NBS", last_name)

 Concatenated_data <- as.data.frame(files)

 begin <- which(colnames(Concatenated_data) ==  "number_of_branches")
 Concatenated_data_stat <- Concatenated_data[, begin:ncol(Concatenated_data)]
 Concatenated_data_stat <- apply(Concatenated_data_stat, 2, as.numeric)
 remove <- which(is.na(rowSums(Concatenated_data_stat)))
 Concatenated_data <- Concatenated_data[-remove, ]

 one <- subset(Concatenated_data, combo =="01")
 two <- subset(Concatenated_data, combo =="02")
 five <- subset(Concatenated_data, combo =="05")
 six <- subset(Concatenated_data, combo =="06")

 crop <- min(sapply(list(one, two, five, six), nrow))

 one <- one[1:crop, ]
 two <- two[1:crop, ]
 five <- five[1:crop, ]
 six <- six[1:crop, ]

 Concatenated_data2 <- rbind(one, two, five, six)
 res <- list(Concatenated_data, Concatenated_data2, crop)
 names(res) <- c("Concatenated_data", "Concatenated_data_crop", "crop")
 return(res)
 
}

path <- "~/Box Sync/Four model compare third run/Module 2"
Concatenated_data0 <- concatenate(path)
print(Concatenated_data0[[3]])
path_save <- "~/Box Sync/Four model compare third run"
Concatenated_data <- Concatenated_data0[[1]]
save(Concatenated_data, file = paste0(path_save,
                                     "/Four_model_compare_results_not_cropped.Rdata"))
Concatenated_data <- Concatenated_data0[[2]]
save(Concatenated_data, file=paste0(path_save,
                                   "/Four_model_compare_results_", 
                                   format(Sys.time(), format="%d_%b_%Y"),
                                   "_crop_to_", Concatenated_data0[[3]],".Rdata"))

### Repeated for extinct
path <- "~/Box Sync/Four model compare third run/Module 2 extinct"
Concatenated_data <- concatenate(path)[[1]]
path_save <- "~/Box Sync/Four model compare third run"

save(Concatenated_data, file=paste0(path_save,
                                   "/Four_model_compare_results_extinct_", 
                                   format(Sys.time(), format="%d_%b_%Y"),".Rdata"))

Diagnostics

#objects()
#head(Concatenated_data)
#names(Concatenated_data)
BL <- scale(log(as.numeric(as.character(Concatenated_data$Pylo_diversity_is_sum_of_BL))), scale=FALSE, center=TRUE)
NaNs produced
#25:54, ! 39, 
load('~/Dropbox/Nature_FARM_publication_prep/Code for publication/RF after bruno/Four_model_compare_results_23_Oct_2017_crop_to_10323.Rdata')
for(i in 25:54){
s <- as.numeric(as.character(Concatenated_data[,i]))
#range01 <- function(x){(x-min(x))/(max(x)-min(x))}
Concatenated_data[,i] <- scale(s)
}
#head(Concatenated_data)
#Concatenated_data$combo
M1 <- subset(Concatenated_data, combo == "01")
M2 <- subset(Concatenated_data, combo == "02")
M3 <- subset(Concatenated_data, combo == "05")
M4 <- subset(Concatenated_data, combo == "06")
png("scaled stats per model.png", height=247, width=89, units = "mm", res = 1000)
par(mfrow=c(4,1), mar=c(2,5,2,2))
barplot(colMeans(M1[,26:54], na.rm = FALSE, dims = 1), col=rainbow(32), xlim=c(-1,1), las=2, main="Basic", horiz=TRUE, names.arg=seq(1,29), col.lab = 0.5)
grid()
barplot(colMeans(M2[,26:54], na.rm = FALSE, dims = 1), col=rainbow(32), xlim=c(-1,1), las=2, main="+Diffusion", horiz=TRUE, names.arg=seq(1,29))
grid()
barplot(colMeans(M3[,26:54], na.rm = FALSE, dims = 1), col=rainbow(32), xlim=c(-1,1), las=2, main="+Takeover", horiz=TRUE, names.arg=seq(1,29))
grid()
barplot(colMeans(M4[,26:54], na.rm = FALSE, dims = 1), col=rainbow(32), xlim=c(-1,1), las=2, main="+Diffusion \n +Takeover", horiz=TRUE, names.arg=seq(1,29))
grid()
dev.off()
null device 
          1 
load("Four_model_compare_results_02_Aug_2017_crop_to_6128.Rdata")
extant <- Concatenated_data
extant

 load("Four_model_compare_results_extinct_02_Aug_2017.Rdata")
extinct <- Concatenated_data
extinct

head(extant)
head(extinct)
for(i in c(3:22)){
    extinct[which(is.nan(as.numeric(as.character(extinct[, i]))) == TRUE), i] <- NA
}

for(i in c(3:22)){
    extant[which(is.nan(as.numeric(as.character(extant[, i]))) == TRUE), i] <- NA
}




xlimit <- c(0,1)
ylimit <- c(0,3000)
maincex <- 0.9

png(file="Global_success_rate_per_parameter.png", width=8.5, height=11, units="in", res=300)

par(mfrow=c(5,4), mar=c(3,3,3,0))


hist(as.numeric(as.character(extinct[,3])), main="speciation of F in F env", col=adjustcolor("firebrick", alpha=0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[,3])), main="speciation of F in F env", col=adjustcolor("cornflowerblue", alpha=0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)


hist(as.numeric(as.character(extinct[,4])), main="speciation of D in F env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[,4])), main="speciation of D in F env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)


hist(as.numeric(as.character(extinct[,5])), main="speciation of F in D env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[,5])), main="speciation of F in D env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

hist(as.numeric(as.character(extinct[,6])), main="speciation of D in D env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[,6])), main="speciation of D in D env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

#######

hist(as.numeric(as.character(extinct[, 7])), main="extinction of F in F env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 7])), main="extinction of F in F env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)


hist(as.numeric(as.character(extinct[, 8])), main="extinction of D in F env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 8])), main="extinction of D in F env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

hist(as.numeric(as.character(extinct[, 9])), main="extinction of F in D env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 9])), main="extinction of F in D env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

hist(as.numeric(as.character(extinct[, 10])), main="extinction of D in D env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 10])), main="extinction of D in D env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

######

hist(as.numeric(as.character(extinct[, 11])), main="arisal of F in F env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 11])), main="arisal of F in F env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)



hist(as.numeric(as.character(extinct[, 12])), main="arisal of D in F env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 12])), main="arisal of D in F env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

hist(as.numeric(as.character(extinct[, 13])), main="arisal of F in D env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 13])), main="arisal of F in D env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

hist(as.numeric(as.character(extinct[, 14])), main="arisal of D in D env", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 14])), main="arisal of D in D env", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

######

hist(as.numeric(as.character(extinct[, 15])), main="NOPE -- Diffusion: source F, target F", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= c(0,18000), cex.main= maincex)
hist(as.numeric(as.character(extant[, 15])), main="NOPE -- Diffusion: source F, target F", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= c(0,18000), cex.main= maincex, add=TRUE)



hist(as.numeric(as.character(extinct[, 16])), main="Diffusion: source D, target F", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 16])), main="Diffusion: source D, target F", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

hist(as.numeric(as.character(extinct[, 17])), main="Diffusion: source F, target D", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 17])), main="Diffusion: source F, target D", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

hist(as.numeric(as.character(extinct[, 18])), main="NOPE -- Diffusion: source D, target D", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= c(0,18000), cex.main= maincex)
hist(as.numeric(as.character(extant[, 18])), main="NOPE -- Diffusion: source D, target D", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= c(0,18000), cex.main= maincex, add=TRUE)

####

hist(as.numeric(as.character(extinct[, 19])), main="Takeover: source F, target F", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 19])), main="Takeover: source F, target F", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)



hist(as.numeric(as.character(extinct[, 20])), main="Takeover: source D, target F", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 20])), main="Takeover: source D, target F", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

hist(as.numeric(as.character(extinct[, 21])), main="Takeover: source F, target D", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 21])), main="Takeover: source F, target D", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)

hist(as.numeric(as.character(extinct[, 22])), main="Takeover: source D, target D", col=adjustcolor("firebrick", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex)
hist(as.numeric(as.character(extant[, 22])), main="Takeover: source D, target D", col=adjustcolor("cornflowerblue", alpha= 0.7), breaks=100, border=NA, xlim= xlimit, ylim= ylimit, cex.main= maincex, add=TRUE)


dev.off()

Random Forest Analysis

Training/Building

The procedures described so far have outlined the creation of a dataset that includes the input and output variables for each replicate simulation. When taken as a whole, that dataset describes a distribution of possible numerical outcomes that are possible given the 4 available input types. The purpose of the random forest machine learning algorithm (Breiman 2001, Liaw and Wiener 2015) was to correlate input and output variables as a means of categorizing simulation outputs according to the type of input that had created them. We used 70% of our available simulation data to build the random forest, the remaining 30% to test the forest’s accuracy and precision at inferring input categories, and then used that trained and vetted algorithm to infer the most likely input category for real world human output data.

This random forest model was trained by building a forest of decision trees. Each decision tree describes a series of ordered dichotomous decisions that categorize the 12 output statistics into one of 4 input types. Individual trees were built by first taking a randomly sized and randomly selected sample of rows (replicate simulations) and columns (output variables) from the full simulation dataset to create a unique subset dataset. That unique dataset was then subjected to a bagging model to rank the variables in order of importance and build a decision tree off of that rank. We repeated this process, with replacement, to build 3000 individual decision trees sampled from 70% (n = 40000 x 0.7 = 28000) of the total simulation data.

Testing

The random forest was vetted using the remaining 30% of the simulation data reserved for model testing. Rather than building decision trees with these data, we fed each replicate through the recently built forest so it could classify outputs and then compare those inferred input types to the known input types to the to see how frequently the forest returned the correct classification. The accuracy and precision of these tests are reported in a confusion matrix showing the number of matches that were correctly or incorrectly made between inputs and outputs for each model . Our trained random forest algorithm could correctly match output statistics with input mechanisms with very good accuracy . It is noteworthy that there was very little confusion between the two competing hypothesized mechanisms, diffusion and diffusion by takeover. If the trained random forest algorithm is presented with a simulation created by diffusion, if is very unlikely to be classify it as coming from diffusion by takeover.

Matching

The structure of our simulation outputs where designed to mimic the structure of our real cultural data so that the two could be compared directly. In the same way the output for replicate simulations where processed, the phylogeny and spatial pattern of extant human cultures were summarized using 12 summary statistics passed to the trained random forest algorithm. The random forest assigned the most likely mechanism to have caused that human’s known historical trajectory. It does this by asking every dichotomous decision tree in the random forest to vote on which hypothesized mechanism created the output provided by the data and then tallying those votes into a consensus vote. Our consensus vote showed that over 50% of the trees believe that the current configuration of agriculture across the globe is the result of demic diffusion augmented by cultural diffusion. About 35% of the trees assigned known global trend to the basic model that includes only demic diffusion, while the other two models received almost no votes. This suggest that if we are wrong that demic and cultural diffusion contributed to current distributions of agriculturist cultures, the next most likely answer is that it diffusion created these patterns with no help from cultural diffusion.

Run a single random forest on available outputs

# Packages
library(randomForest)

# Radom Forest Function
RF <- function(table_sim, table_real, ntree = 2000, stats_remove = NULL,
              repetitions = 1) {
 # table_sim = table croped 

 begin <- which(colnames(table_sim) ==  "number_of_branches")
 table_sim_data <- table_sim[, begin:ncol(table_sim)]
 table_sim_data <- apply(table_sim_data, 2, as.numeric)
 if (any(is.infinite(table_sim_data))) {
   stop("There is infinite values in the simulation statistics")
 }
 rf_data <- data.frame("Model" = table_sim$combo, table_sim_data)
 rf_data_real <- data.frame(table_real)

 if (!is.null(stats_remove)) {
   rf_data <- rf_data[, -stats_remove]
   rf_data_real <- rf_data_real[, -stats_remove]
 }

 fun <- function(x, y, per = .33) {
   sample(which(y$Model == x), round(table(y$Model)[1]*per))
 }

 results <- matrix(nrow = repetitions, ncol = length(table_real) * 6 + 21)
 for (i in 1:repetitions) {
   sub.test <- unlist(lapply(as.list(paste0(0, c(1,2,5,6))), fun,
                             y = rf_data))
   test2 <- rf_data[sub.test, 2:ncol(rf_data)]
   test1 <- rf_data[sub.test, 1]
   train <- rf_data[-sub.test, ]

   fit <- randomForest(Model ~ ., data = train, xtest = test2, 
                       ytest = test1, importance = TRUE, 
                       ntree = ntree, keep.forest = TRUE,
                       replace = TRUE)
  #dev.new()
  #plot(fit)
  
   predictions <- predict(fit, rf_data_real, type = "prob")
  
   var_import <- importance(fit)

   error <- mean(fit$test$confusion[, 5])
   confusion <- as.numeric(fit$test$confusion[, 1:4])
   names(confusion) <- paste0("Confusion_", 
                              rep(colnames(fit$test$confusion[, 1:4]),
                                  each = 4),
                              rownames(fit$test$confusion[, 1:4]))
   predictions_prob <- as.numeric(predictions)
   names(predictions_prob) <- paste0("Prediction_prob", colnames(predictions))
   var_import_vec <- as.numeric(var_import)
   names(var_import_vec) <- paste0("var_imp_", 
                                   rep(colnames(var_import), 
                                       each = nrow(var_import)),
                                   "_",
                                   rownames(var_import))
   vec <- c(error, confusion, predictions_prob, var_import_vec)
   
   results[i, ] <- vec
 }
 colnames(results) <- names(vec)
 colnames(results)[1] <- "Error_test"
 results <- cbind(1:nrow(results), results)
 colnames(results)[1] <- "Replicate"
 return(results)
}

# Multiple Random Forest
RF_mult <- function(table_sim, table_real_mult, ntree = 2000, 
                   stats_remove = NULL, repetitions = 1) {
 n_r <- nrow(table_real_mult)
 n <- repetitions * n_r
 n_col <- ncol(table_real_mult) * 6 + 22
 results <- matrix(nrow = n, ncol = n_col)
 x <- 0
 for (i in seq(1, n, repetitions)) {
   print(paste0("randomForest start for tree", i))
   x <- x + 1
   temp <- RF(table_sim, 
              table_real_mult[x, , drop = FALSE],
              ntree = ntree, 
              repetitions = repetitions, 
              stats_remove = stats_remove)
   results[i:(i + repetitions - 1), ] <- temp
 }
 tree <- rep(1:n_r, each = repetitions)
 results <- cbind(tree, results)
 colnames(results) <- c("Tree", colnames(temp))
 return(results)
}

# Accumulation function
RF_acum <- function(table_sim, table_real_mult, ntree = 2000, 
                   stats_remove = NULL, repetitions = 1,
                   resolution = 100, minimun = 100) {
 sequence <- seq(minimun, nrow(table_sim), resolution)[2]
 n_col <- ncol(table_real_mult) * 6 + 24 
 n_r <- nrow(table_real_mult)
 n_seq <- length(sequence)
 n <- repetitions * n_r * n_seq
 results <- matrix(nrow = n, ncol = n_col)
 lottery1 <- which(table_sim$combo == "01")
 lottery2 <- which(table_sim$combo == "02")
 lottery5 <- which(table_sim$combo == "05")
 lottery6 <- which(table_sim$combo == "06")
 sub1 <- sample(lottery1, sequence[1]/4, replace = FALSE)
 sub2 <- sample(lottery2, sequence[1]/4, replace = FALSE)
 sub5 <- sample(lottery5, sequence[1]/4, replace = FALSE)
 sub6 <- sample(lottery6, sequence[1]/4, replace = FALSE)
 for (i in 1:n_seq) {
   if (i != 1) {
     sub1 <- c(sub1, sample(lottery1[-sub1], sequence[1]/4, replace = FALSE))
     sub2 <- c(sub2, sample(lottery2[-sub2], sequence[1]/4, replace = FALSE))
     sub5 <- c(sub5, sample(lottery5[-sub5], sequence[1]/4, replace = FALSE))
     sub6 <- c(sub6, sample(lottery6[-sub6], sequence[1]/4, replace = FALSE))
   }
  print(paste0("MULTI --------------------------- RF -- ", i))
   temp <- RF_mult(table_sim[c(sub1, sub2, sub5, sub6), ], table_real_mult,
                   ntree = ntree, repetitions = repetitions,
                   stats_remove = stats_remove)
   n_temp <- nrow(temp)
   results[1:n_temp + (n_temp * (i - 1)), ] <- cbind(rep(sequence[i],
                                                         n_temp), temp)
 }
 colnames(results) <- c("Subsample_size", colnames(temp))
 return(results)
}

# TEST FINAL (RUN ONLY THIS)
# Load data
setwd("~/Dropbox/Nature_FARM_publication_prep/Code for publication/RF after bruno")
load('Four_model_compare_results_23_Oct_2017_crop_to_10323.Rdata')
load("real.analysis_mult.RData")

table_sim = Concatenated_data
table_real = real.analysis.mult[[1]][[1]]
table_real_mult = do.call(rbind, lapply(real.analysis.mult, 
                                       function(x){x[[1]]}))
dim(table_real_mult[,,drop=FALSE])
#start time
time_start <- Sys.time()

# Change the parameters as you wish
## table_real_mult is the distribution of possible trees provided by MPI
## table_sim is our simulated data
res_acum <- RF_acum(table_sim, table_real_mult[1:200,,drop=FALSE], ntree = 10000, 
                   stats_remove = NULL, repetitions = 1,
                   resolution = 5000, minimun = 9500)
                   
#stop time
time_stop <- Sys.time()
difftime(time_stop, time_start)                   

save(res_acum, file="Random_Forest_output_data_for_publication_5_February_2018.Rdata")

Visualize outputs from Random Forest analysis

# load outputs from RF runs
load("/Users/Ty/Desktop/small_RF_out.Rdata")
a <- res_acum

load("/Users/Ty/Desktop/10_more_tree_out.Rdata")
b <- res_acum

load("/Users/Ty/Desktop/10_tree_out.Rdata")
c <- res_acum

#object called res_acum
res_acum <- rbind(b,c)

load("Random_Forest_output_data_for_publication_2_February_2018.Rdata")
png("overall_error_per_sample_size.png", width = 11, height = 8.5, res = 300, units = "in")
plot(aggregate(x = res_acum[, 4], by = list((res_acum[, 1])), FUN = mean), type="l", ylim=c(0,0.5), ylab="overall confusion error", xlab="sample size")
dev.off()
png("predictions.png", width = 11, height = 8.5, res = 300, units = "in")
par(mar=c(5,6,2,1))

# Change argument FUN to sd to obtain the standard deviation
plot(aggregate(x = res_acum[, 4], by = list((res_acum[, 1])), FUN = mean), type = "n", ylim=c(0,1), ylab="probability that our known cultural phylogeny came \n from each type of simulated mechanism", xlab="number of simulation replicates", cex.axis=1.5, cex.lab=1.5)


basic_mean <- aggregate(x = res_acum[, 21], by = list((res_acum[, 1])), FUN = mean)
diffusion_mean <- aggregate(x = res_acum[, 22], by = list((res_acum[, 1])), FUN = mean)
TO_mean <- aggregate(x = res_acum[, 23], by = list((res_acum[, 1])), FUN = mean)
both_mean <- aggregate(x = res_acum[, 24], by = list((res_acum[, 1])), FUN = mean)
                   
basic_sd <- aggregate(x = res_acum[, 21], by = list((res_acum[, 1])), FUN = sd)
diffusion_sd <- aggregate(x = res_acum[, 22], by = list((res_acum[, 1])), FUN = sd)
TO_sd <- aggregate(x = res_acum[, 23], by = list((res_acum[, 1])), FUN = sd)
both_sd <- aggregate(x = res_acum[, 24], by = list((res_acum[, 1])), FUN = sd)


polygon(x=c(basic_mean[,1], rev(basic_mean[,1])), y=c(basic_mean[,2] + basic_sd[,2], rev(basic_mean[,2] - basic_sd[,2])), col=adjustcolor("orange", alpha=0.5), border=NA)
polygon(x=c(diffusion_mean[,1], rev(diffusion_mean[,1])), y=c(diffusion_mean[,2] + diffusion_sd[,2], rev(diffusion_mean[,2] - diffusion_sd[,2])), col=adjustcolor("cornflowerblue", alpha=0.5), border=NA)
polygon(x=c(TO_mean[,1], rev(TO_mean[,1])), y=c(TO_mean[,2] + TO_sd[,2], rev(TO_mean[,2] - TO_sd[,2])), col=adjustcolor("pink", alpha=0.5), border=NA)
polygon(x=c(both_mean[,1], rev(both_mean[,1])), y=c(both_mean[,2] + both_sd[,2], rev(both_mean[,2] - both_sd[,2])), col=adjustcolor("darkgreen", alpha=0.5), border=NA)



lines(basic_mean,  col="orange")
lines(diffusion_mean,  col="cornflowerblue")
lines(TO_mean,  col="pink")
lines(both_mean,  col="darkgreen")

labs <- c("Basic", "+Diffusion", "+Takeover", "+Diffusion +Takeover")
legend("topright", legend=labs, col=c("orange", "cornflowerblue", "pink", "darkgreen"), lty=1, cex=1.5, lwd=10)

dev.off()
png("last_step_predictions.png", width = 11, height = 8.5, res = 300, units = "in")
par(mar=c(5,6,2,1))
this <- length(basic_mean[,1])

plot(x=c(0,5), y=c(0,1), type="n",  xaxt="n", ylab="probability that our known cultural phylogeny came \n from each type of simulated mechanism", xlab="", xlim=c(0.5,4.5))
polygon(x=c(0.75,0.75,1.25,1.25), y=c(basic_mean[this,2] - basic_sd[this,2], basic_mean[this,2] + basic_sd[this,2], basic_mean[this,2] + basic_sd[this,2], basic_mean[this,2] - basic_sd[this,2]), col = adjustcolor("orange", alpha=0.5), border=NA )
polygon(x=c(1.75,1.75,2.25,2.25), y=c(diffusion_mean[this,2] - diffusion_sd[this,2], diffusion_mean[this,2] + diffusion_sd[this,2], diffusion_mean[this,2] + diffusion_sd[this,2], diffusion_mean[this,2] - diffusion_sd[this,2]), col = adjustcolor("cornflowerblue", alpha=0.5), border=NA)
polygon(x=c(2.75,2.75,3.25,3.25), y=c(TO_mean[this,2] - TO_sd[this,2], TO_mean[this,2] + TO_sd[this,2], TO_mean[this,2] + TO_sd[this,2], TO_mean[this,2] - TO_sd[this,2]), col = adjustcolor("pink", alpha=0.5), border=NA)
polygon(x=c(3.75,3.75,4.25,4.25), y=c(both_mean[this,2] - both_sd[this,2], both_mean[this,2] + both_sd[this,2], both_mean[this,2] + both_sd[this,2], both_mean[this,2] - both_sd[this,2]), col = adjustcolor("darkgreen", alpha=0.5), border=NA)


lines(x=c(0.5, 1.5), c(basic_mean[this,2], basic_mean[this,2]),  col="orange")
lines(x=c(1.5, 2.5), c(diffusion_mean[this,2], diffusion_mean[this,2]),  col="cornflowerblue")
lines(x=c(2.5, 3.5), c(TO_mean[this,2], TO_mean[this,2]),  col="pink")
lines(x=c(3.5, 4.5), c(both_mean[this,2], both_mean[this,2]),  col="darkgreen")

legend("topright", legend=labs, col=c("orange", "cornflowerblue", "pink", "darkgreen"), lty=1, cex=1.5, lwd=10)

dev.off()
#colnames(res_acum)

long <- length(Confusion_0101_mean[,1])
Confusion_sum_01 <- aggregate(x = res_acum[, 5:8], by = list((res_acum[, 1])), FUN = mean)
Confusion_sum_02 <- aggregate(x = res_acum[, 9:12], by = list((res_acum[, 1])), FUN = mean)
Confusion_sum_03 <- aggregate(x = res_acum[, 13:16], by = list((res_acum[, 1])), FUN = mean)
Confusion_sum_04 <- aggregate(x = res_acum[, 17:20], by = list((res_acum[, 1])), FUN = mean)

percent_one <- (Confusion_sum_01[,2:5]/sum(Confusion_sum_01[,2:5])) * 100
percent_two <- (Confusion_sum_02[,2:5]/sum(Confusion_sum_02[,2:5])) * 100
percent_three <- (Confusion_sum_03[,2:5]/sum(Confusion_sum_03[,2:5])) * 100
percent_four <- (Confusion_sum_04[,2:5]/sum(Confusion_sum_04[,2:5])) * 100


confusion <- matrix(c(percent_one, percent_two, percent_three, percent_four), 4,4)

labs <- c("Basic", "+Diffusion", "+Takeover", "+Diffusion",  "+Takeover")
colors1 <- colorRampPalette(colors = c("grey95", "grey40"))


png("confusion_matrix.png", width = 8.5, height = 8.5, res = 600, units = "in")
par(mar=c(8,8,1,1))
plot(0,0,xlim=c(-0.2,1.4), ylim=c(-0.2,1.4), xaxt="n", xlab="", yaxt="n", ylab="" , bty="n")
#image(prop, col = colors1(20), axes=FALSE)
axis(1, at=c(0, .4, .8, 1.2, 1.3), labels=labs, tick = FALSE, line = FALSE, cex.axis = 1, pos = -.19, las=2)
axis(2, at=rev(c(-0.05, 0.05, .4, .8, 1.2)), labels=labs, tick = FALSE, line = FALSE, cex.axis = 1, las=2)
mtext("percent of time that RF identifies input model as each model type", side = 1, padj = 10, cex = 1)
mtext("known model type given to random forest", side = 2, padj = -10, cex = 1)



for(i in 1:4) {
  for(j in 4:1) {
    xs <- c(0, .4, .8, 1.2)[i]
    ys <- rev(c(0, .4, .8, 1.2))[j]
    polygon(x=c(xs-0.2, xs-0.2, xs+0.2, xs+0.2), y=c(ys-0.2, ys+0.2, ys+0.2, ys-0.2), col=colors1(100)[round(as.numeric(confusion[i, j]), 1)+1])
    if(i == j){text(x = xs, y = ys, paste0(round(as.numeric(confusion[i, j]), 2), "%"), cex = 2.2, col="limegreen")}else{(text(x = xs, y = ys, paste0(round(as.numeric(confusion[i, j]), 2), "%"), cex = 2.2, col="black"))}
  }
}
# Variables importance

imp <- importance(fit)
imp <- apply(imp, 2, function(x) (x - min(x))/(max(x) - min(x)))
imp <- imp[sort(imp[, 5], index.return = TRUE, decreasing = TRUE)$ix, ]

as.character(replace[, 6])
load(as.character(replace[90, 6]))
imp <- importance(fit)

names <- rownames(imp)
names[names == "spatial.tests.fora"] <- "Space F"
names[names == "spatial.tests.dom"] <- "Space D"
names[names == "sprate"] <- "Sp(ratio)"
names[names == "transition_from_trait_1_to_2"] <- "TR(1-2)"
names[names == "transition_from_trait_2_to_1"] <- "TR(2-1)"
names[names == "Phylogenetic_signal"] <- "PhySig(D)"
names[names == "Evolutionary_distinctiveness_sum"] <- "EDsum"
names[names == "Pylo_diversity_is_sum_of_BL"] <- "PDsum"
names[names == "transition_rate_ratio_1to2_over_2to1"] <- "TR(ratio)"
names[names == "gamma"] <- "Gamma"
names[names == "mean_Phylogenetic_isolation"] <- "MPI"
names[names == "extrate"] <- "Ext(ratio)"
names[names == "average_phylogenetic_diversity_is_mean_of_BL"] <- "PDmean"
names[names == "extinction_per_speciation"] <- "DR"
names[names == "variance_Phylogenetic_isolation"] <- "VPI"
names[names == "F_quadratic_entropy_is_sum_of_PD"] <- "F"
names[names == "Mean_pairwise_distance"] <- "MPD"
names[names == "variance_Pylo_diversity_is_variance_of_BL"] <- "PDvar"
names[names == "variance_pairwise_distance"] <- "VPD"


x_length <- length(levels(replace[,1]))

######
# make matrix








#####
#plot from matrix










pdf(file="heat map of variable importance.pdf", width=11, height=8.5)

par(mar=c(4,10,4,1))
plot(x = seq(1, x_length, by=1), y = rep(0, x_length), type="n", ylim=c(0,20), ylab="", yaxt="n", xaxt="n", xlab="", xlim=c(1, x_length*100))

axis(2, label=names, at=seq(0.5,length(names)-.5), las=2)


for(i in 1:length(as.character(replace[, 6]))){
    
    load(as.character(replace[i, 6]))
    imp <- importance(fit)
#imp <- apply(imp, 2, function(x) (x - min(x))/(max(x) - min(x)))
    #i <- 10
    
x_range <- c(i, i, i+1, i+1)    
percent <- as.numeric(ceiling(imp[,5] ))
percent[which(percent == 0)] <- NA
colors <- cm.colors(100)[percent]

for(j in 0:19){
    y_range <- c(j, j+1, j+1, j)
polygon(x = x_range, y = y_range, col= colors[j+1], border=NA)
}

}

abline(v=seq(0,1000, by=100))

axis(1, label=format(unique(replace[,7]), format="%d %b %Y"), at=seq(50,950, by=100)[1:length(unique(replace[,7]))])
dev.off()
importance(fit)
# Variables importance

imp <- importance(fit)
imp <- apply(imp, 2, function(x) (x - min(x))/(max(x) - min(x)))
imp <- imp[sort(imp[, 5], index.return = TRUE, decreasing = TRUE)$ix, ]


names <- rownames(imp)
names[names == "spatial.tests.fora"] <- "Space F"
names[names == "spatial.tests.dom"] <- "Space D"
names[names == "sprate"] <- "Sp(ratio)"
names[names == "transition_from_trait_1_to_2"] <- "TR(1-2)"
names[names == "transition_from_trait_2_to_1"] <- "TR(2-1)"
names[names == "Phylogenetic_signal"] <- "PhySig(D)"
names[names == "Evolutionary_distinctiveness_sum"] <- "EDsum"
names[names == "Pylo_diversity_is_sum_of_BL"] <- "PDsum"
names[names == "transition_rate_ratio_1to2_over_2to1"] <- "TR(ratio)"
names[names == "gamma"] <- "Gamma"
names[names == "mean_Phylogenetic_isolation"] <- "MPI"
names[names == "extrate"] <- "Ext(ratio)"
names[names == "average_phylogenetic_diversity_is_mean_of_BL"] <- "PDmean"
names[names == "extinction_per_speciation"] <- "DR"
names[names == "variance_Phylogenetic_isolation"] <- "VPI"
names[names == "F_quadratic_entropy_is_sum_of_PD"] <- "F"
names[names == "Mean_pairwise_distance"] <- "MPD"
names[names == "variance_Pylo_diversity_is_variance_of_BL"] <- "PDvar"
names[names == "variance_pairwise_distance"] <- "VPD"


png("var_import_all.png", width = 25, height = 25, unit="in", res=300)
par(mar = c(10, 18, 1, 1))
plot(x = rev(imp[, 5]), y = 1:nrow(imp), type = "l", yaxt = "n", 
     ylab = "", xlab = "Variable Importance",
     xlim = c(0, 1), lwd = 2, cex.lab = 4)
for (i in 1:nrow(imp)) {
  abline(h = i, lty = 3, col = "gray80")
}
abline(v = seq(0, 1, 1/19), lty = 3, col = "gray80")

lines(x = rev(imp[, 4]), y = 1:nrow(imp), col = "darkgreen", lwd = 2)
lines(x = rev(imp[, 3]), y = 1:nrow(imp), col = "red", lwd = 2)
lines(x = rev(imp[, 2]), y = 1:nrow(imp), col = "blue", lwd = 2)
lines(x = rev(imp[, 1]), y = 1:nrow(imp), col = "darkorange1", lwd = 2)
lines(x = rev(imp[, 5]), y = 1:nrow(imp), lwd = 3)

points(x = rev(imp[, 4]), y = 1:nrow(imp), col = "darkgreen", cex = 2)
points(x = rev(imp[, 3]), y = 1:nrow(imp), col = "red", cex = 2)
points(x = rev(imp[, 2]), y = 1:nrow(imp), col = "blue", cex = 2)
points(x = rev(imp[, 1]), y = 1:nrow(imp), col = "darkorange1", cex = 2)
points(x = rev(imp[, 5]), y = 1:nrow(imp), pch = 20, cex = 3)


text(y = 1:nrow(imp), x = par("usr")[1] - .17, labels = rev(names),
     srt = 0, pos = 4, xpd = T, cex = 4)
dev.off()
par(mfrow=c(2,3))

# Box plots
boxplot(spatial.tests.fora ~ Model, data = data.analysis.comp3)
abline(h = a$spatial.tests.fora, col = "red", lty = 2)

boxplot(spatial.tests.dom ~ Model, data = data.analysis.comp3)
abline(h = a$spatial.tests.fora, col = "red", lty = 2)

boxplot(log(sprate) ~ Model, data = data.analysis.comp3, ylim = c(-10, 10))
abline(h = log(a$sprate), col = "red", lty = 2)

boxplot(log(extrate) ~ Model, data = data.analysis.comp3, ylim = c(-10, 10))
abline(h = log(a$extrate), col = "red", lty = 2)

boxplot(log(transition_rate_ratio_1to2_over_2to1) ~ Model, data = data.analysis.comp3)
abline(h = log(a$sprate), col = "red", lty = 2)

boxplot(Phylogenetic_signal ~ Model, data = data.analysis.comp3, ylim = c(0, 1))
abline(h = a$Phylogenetic_signal, col = "red", lty = 2)
#build a data tracking table to track parameter changes through time

str(fit)

y
LS0tCnRpdGxlOiAnRC1wbGFjZSBGQVJNIGRvY3VtZW50YXRpb246IE1vZHVsZSAzJwphdXRob3I6ICJUeSBUdWZmLCBCcnVubyBWaWxlbGEsIGFuZCBDYXJsb3MgQm90ZXJvIgpkYXRlOiAncHJvamVjdCBiZWdhbjogMTUgTWF5IDIwMTYsIGRvY3VtZW50IHVwZGF0ZWQ6IGByIHN0cmZ0aW1lKFN5cy50aW1lKCksIGZvcm1hdAogID0gIiVkICVCICVZIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0CmJpYmxpb2dyYXBoeTogRkFSTSBwYWNrYWdlLmJpYgotLS0KCiMgTW9kdWxlIDM6IEFuYWx5c2luZyByZXN1bHRzIHByb2R1Y2UgZnJvbSBNb2R1bGVzIDEgYW5kIDIKCmBgYHtyfQpsaWJyYXJ5KHBuZykKYGBgCgoKIyMgT3JnYW5pemUgZGF0YQpgYGB7ciwgZXZhbD1GQUxTRX0KIyBBIGZyZXF1ZW50IHNjZW5hcmlvIGluIG91ciBhbmFseXNpcyBjb2RlIGlzIHRoYXQgd2UgbmVlZCB0byByZWFkIHRob3VzYW5kcyBvZiAKIyBvdXRwdXQgZmlsZXMgaW50byBhIHNpbmdsZSB0YWJsZS4gCiMgVGhlcmUgYXJlIGEgY291cGxlIGZsYXZvcnMgb2YgZmlsZSBuYW1lIHRoYXQgbmVlZCB0byBiZSBkZWFsdCB3aXRoIGFuZCB0aGUgCiMgb3V0cHV0IGZpbGUgbmVlZHMgdG8gaW5jbHVkZSBhIGNvbHVtbiB3aXRoIHRoZSBmdWxsIGZpbGUgbmFtZSBmb3IgcmVmZXJlbmNlIAojIGxhdGVyLiBXZSB3YW50IHRvIHNhdmUgdGhlIGZpbmFsIHRhYmxlIGFzIGl0J3Mgb3duIGZpbGUgdG8gYSBmb2xkZXIgb3V0c2lkZSBvZiAKIyB0aGUgb25lIHdlIGp1c3QgcmVhZC4gCgojIyBIZXJlIGlzIG9uZSBmbGF2ZXIgd2hlcmUgdGhlIGlucHV0IGlzIHR3byBmb2xkZXJzLCBvbmUgd2l0aCBleHRhbnQgCiMgc2ltdWxhdGlvbnMgYW5kIG9uZSB3aXRoIGV4dGluY3Qgc2ltdWxhdGlvbnMuIE5lZWQgdG8gcmVhZCB0aGUgZmlsZXMgYW5kIAojIHByb2R1Y2UgdHdvIHRhYmxlcywgb25lIGZvciBleHRpbmN0IGFuZCBvbmUgZm9yIGV4dGFudC4gCgojIyBGaXJzdCBjb25zb2xpZGF0ZSB0aGUgYXZhaWxhYmxlIGZpbGVzIGludG8gYSBzaW5nbGUgdGFibGUKY29uY2F0ZW5hdGUgPC0gZnVuY3Rpb24ocGF0aCkgewogYXZhaWxhYmxlIDwtIGxpc3QuZmlsZXMocGF0aCwgZnVsbC5uYW1lcyA9IFRSVUUpCiBuYW1lIDwtIHVubGlzdChzdHJzcGxpdChhdmFpbGFibGVbMV0sIHNwbGl0PSJfIikpCiBuIDwtIGxlbmd0aChhdmFpbGFibGUpCiBuX25hbWUgPC0gbGVuZ3RoKG5hbWUpIC0gMQogbG9hZChhdmFpbGFibGVbMV0pCiBuY29sX2ZpbGVzIDwtIGxlbmd0aChTaW1fc3RhdGlzdGljc1tbMV1dKSArIG5fbmFtZQogZmlsZXMgPC0gbWF0cml4KG5yb3cgPSBuLAogICAgICAgICAgICAgICAgIG5jb2wgPSBuY29sX2ZpbGVzKQogc3BsaXRfbmFtZXMgPC0gZnVuY3Rpb24oeCl7dW5saXN0KHN0cnNwbGl0KHgsIHNwbGl0ID0gIl8iKSl9CiBuYW1lX2F2YWlsYWJsZSA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkoYXMubGlzdChhdmFpbGFibGUpLCBzcGxpdF9uYW1lcykpCiBmaWxlc1ssIDE6bl9uYW1lXSA8LSBuYW1lX2F2YWlsYWJsZVssIC1uY29sKG5hbWVfYXZhaWxhYmxlKV0KCiBmb3IgKGkgaW4gMTpuKSB7CiAgIGVycm9yIDwtIHRyeShsb2FkKGF2YWlsYWJsZVtpXSksIHNpbGVudCA9IFRSVUUpCiAgIGlmIChjbGFzcyhlcnJvcikgIT0gInRyeS1lcnJvciIpIHsKICAgICBmaWxlc1tpLCAobl9uYW1lICsgMSk6bmNvbF9maWxlc10gPC0gU2ltX3N0YXRpc3RpY3NbWzFdXQogICB9CiB9CgoKIGZpbGVzIDwtIGZpbGVzWywgLWMoMSwgMiwgMywgNSwgNywgOCwgMTMsIDE4LCAyMywgMjgsIDMzLCAzNSldCiBsYXN0X25hbWUgPC0gY29sbmFtZXMoU2ltX3N0YXRpc3RpY3NbWzFdXSkKIGlmIChpcy5udWxsKGxhc3RfbmFtZSkpIHsKCiAgIGZpbGVzIDwtIGZpbGVzWywgLW5jb2woZmlsZXMpXQogfQogY29sbmFtZXMoZmlsZXMpIDwtICBjKCJTaW1fc3RhdHNfcmVwIiwgImNvbWJvIiwgcGFzdGUwKCJQLnNwZWNpYXRpb24iLCAxOjQpLAogICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiUC5leHRpbmN0IiwgMTo0KSwgIHBhc3RlMCgiUC5kaWZmdXMiLCAxOjQpLAogICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiUC5UTyIsIDE6NCksICBwYXN0ZTAoIlAuQXJpc2FsIiwgMTo0KSwgCiAgICAgICAgICAgICAgICAgICAgICAgInRpbWVzdGVwcyIsICJOQlMiLCBsYXN0X25hbWUpCgogQ29uY2F0ZW5hdGVkX2RhdGEgPC0gYXMuZGF0YS5mcmFtZShmaWxlcykKCiBiZWdpbiA8LSB3aGljaChjb2xuYW1lcyhDb25jYXRlbmF0ZWRfZGF0YSkgPT0gICJudW1iZXJfb2ZfYnJhbmNoZXMiKQogQ29uY2F0ZW5hdGVkX2RhdGFfc3RhdCA8LSBDb25jYXRlbmF0ZWRfZGF0YVssIGJlZ2luOm5jb2woQ29uY2F0ZW5hdGVkX2RhdGEpXQogQ29uY2F0ZW5hdGVkX2RhdGFfc3RhdCA8LSBhcHBseShDb25jYXRlbmF0ZWRfZGF0YV9zdGF0LCAyLCBhcy5udW1lcmljKQogcmVtb3ZlIDwtIHdoaWNoKGlzLm5hKHJvd1N1bXMoQ29uY2F0ZW5hdGVkX2RhdGFfc3RhdCkpKQogQ29uY2F0ZW5hdGVkX2RhdGEgPC0gQ29uY2F0ZW5hdGVkX2RhdGFbLXJlbW92ZSwgXQoKIG9uZSA8LSBzdWJzZXQoQ29uY2F0ZW5hdGVkX2RhdGEsIGNvbWJvID09IjAxIikKIHR3byA8LSBzdWJzZXQoQ29uY2F0ZW5hdGVkX2RhdGEsIGNvbWJvID09IjAyIikKIGZpdmUgPC0gc3Vic2V0KENvbmNhdGVuYXRlZF9kYXRhLCBjb21ibyA9PSIwNSIpCiBzaXggPC0gc3Vic2V0KENvbmNhdGVuYXRlZF9kYXRhLCBjb21ibyA9PSIwNiIpCgogY3JvcCA8LSBtaW4oc2FwcGx5KGxpc3Qob25lLCB0d28sIGZpdmUsIHNpeCksIG5yb3cpKQoKIG9uZSA8LSBvbmVbMTpjcm9wLCBdCiB0d28gPC0gdHdvWzE6Y3JvcCwgXQogZml2ZSA8LSBmaXZlWzE6Y3JvcCwgXQogc2l4IDwtIHNpeFsxOmNyb3AsIF0KCiBDb25jYXRlbmF0ZWRfZGF0YTIgPC0gcmJpbmQob25lLCB0d28sIGZpdmUsIHNpeCkKIHJlcyA8LSBsaXN0KENvbmNhdGVuYXRlZF9kYXRhLCBDb25jYXRlbmF0ZWRfZGF0YTIsIGNyb3ApCiBuYW1lcyhyZXMpIDwtIGMoIkNvbmNhdGVuYXRlZF9kYXRhIiwgIkNvbmNhdGVuYXRlZF9kYXRhX2Nyb3AiLCAiY3JvcCIpCiByZXR1cm4ocmVzKQogCn0KCnBhdGggPC0gIn4vQm94IFN5bmMvRm91ciBtb2RlbCBjb21wYXJlIHRoaXJkIHJ1bi9Nb2R1bGUgMiIKQ29uY2F0ZW5hdGVkX2RhdGEwIDwtIGNvbmNhdGVuYXRlKHBhdGgpCnByaW50KENvbmNhdGVuYXRlZF9kYXRhMFtbM11dKQpwYXRoX3NhdmUgPC0gIn4vQm94IFN5bmMvRm91ciBtb2RlbCBjb21wYXJlIHRoaXJkIHJ1biIKQ29uY2F0ZW5hdGVkX2RhdGEgPC0gQ29uY2F0ZW5hdGVkX2RhdGEwW1sxXV0Kc2F2ZShDb25jYXRlbmF0ZWRfZGF0YSwgZmlsZSA9IHBhc3RlMChwYXRoX3NhdmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiL0ZvdXJfbW9kZWxfY29tcGFyZV9yZXN1bHRzX25vdF9jcm9wcGVkLlJkYXRhIikpCkNvbmNhdGVuYXRlZF9kYXRhIDwtIENvbmNhdGVuYXRlZF9kYXRhMFtbMl1dCnNhdmUoQ29uY2F0ZW5hdGVkX2RhdGEsIGZpbGU9cGFzdGUwKHBhdGhfc2F2ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiL0ZvdXJfbW9kZWxfY29tcGFyZV9yZXN1bHRzXyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdChTeXMudGltZSgpLCBmb3JtYXQ9IiVkXyViXyVZIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIl9jcm9wX3RvXyIsIENvbmNhdGVuYXRlZF9kYXRhMFtbM11dLCIuUmRhdGEiKSkKCiMjIyBSZXBlYXRlZCBmb3IgZXh0aW5jdApwYXRoIDwtICJ+L0JveCBTeW5jL0ZvdXIgbW9kZWwgY29tcGFyZSB0aGlyZCBydW4vTW9kdWxlIDIgZXh0aW5jdCIKQ29uY2F0ZW5hdGVkX2RhdGEgPC0gY29uY2F0ZW5hdGUocGF0aClbWzFdXQpwYXRoX3NhdmUgPC0gIn4vQm94IFN5bmMvRm91ciBtb2RlbCBjb21wYXJlIHRoaXJkIHJ1biIKCnNhdmUoQ29uY2F0ZW5hdGVkX2RhdGEsIGZpbGU9cGFzdGUwKHBhdGhfc2F2ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiL0ZvdXJfbW9kZWxfY29tcGFyZV9yZXN1bHRzX2V4dGluY3RfIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0KFN5cy50aW1lKCksIGZvcm1hdD0iJWRfJWJfJVkiKSwiLlJkYXRhIikpCgoKCgoKYGBgCgoKIyMgRGlhZ25vc3RpY3MKCmBgYHtyfQojb2JqZWN0cygpCiNoZWFkKENvbmNhdGVuYXRlZF9kYXRhKQojbmFtZXMoQ29uY2F0ZW5hdGVkX2RhdGEpCkJMIDwtIHNjYWxlKGxvZyhhcy5udW1lcmljKGFzLmNoYXJhY3RlcihDb25jYXRlbmF0ZWRfZGF0YSRQeWxvX2RpdmVyc2l0eV9pc19zdW1fb2ZfQkwpKSksIHNjYWxlPUZBTFNFLCBjZW50ZXI9VFJVRSkKIzI1OjU0LCAhIDM5LCAKCmxvYWQoJ34vRHJvcGJveC9OYXR1cmVfRkFSTV9wdWJsaWNhdGlvbl9wcmVwL0NvZGUgZm9yIHB1YmxpY2F0aW9uL1JGIGFmdGVyIGJydW5vL0ZvdXJfbW9kZWxfY29tcGFyZV9yZXN1bHRzXzIzX09jdF8yMDE3X2Nyb3BfdG9fMTAzMjMuUmRhdGEnKQoKZm9yKGkgaW4gMjU6NTQpewpzIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKENvbmNhdGVuYXRlZF9kYXRhWyxpXSkpCiNyYW5nZTAxIDwtIGZ1bmN0aW9uKHgpeyh4LW1pbih4KSkvKG1heCh4KS1taW4oeCkpfQoKQ29uY2F0ZW5hdGVkX2RhdGFbLGldIDwtIHNjYWxlKHMpCgoKfQoKI2hlYWQoQ29uY2F0ZW5hdGVkX2RhdGEpCiNDb25jYXRlbmF0ZWRfZGF0YSRjb21ibwpNMSA8LSBzdWJzZXQoQ29uY2F0ZW5hdGVkX2RhdGEsIGNvbWJvID09ICIwMSIpCk0yIDwtIHN1YnNldChDb25jYXRlbmF0ZWRfZGF0YSwgY29tYm8gPT0gIjAyIikKTTMgPC0gc3Vic2V0KENvbmNhdGVuYXRlZF9kYXRhLCBjb21ibyA9PSAiMDUiKQpNNCA8LSBzdWJzZXQoQ29uY2F0ZW5hdGVkX2RhdGEsIGNvbWJvID09ICIwNiIpCgpwbmcoInNjYWxlZCBzdGF0cyBwZXIgbW9kZWwucG5nIiwgaGVpZ2h0PTI0Nywgd2lkdGg9ODksIHVuaXRzID0gIm1tIiwgcmVzID0gMTAwMCkKcGFyKG1mcm93PWMoNCwxKSwgbWFyPWMoMiw1LDIsMikpCmJhcnBsb3QoY29sTWVhbnMoTTFbLDI2OjU0XSwgbmEucm0gPSBGQUxTRSwgZGltcyA9IDEpLCBjb2w9cmFpbmJvdygzMiksIHhsaW09YygtMSwxKSwgbGFzPTIsIG1haW49IkJhc2ljIiwgaG9yaXo9VFJVRSwgbmFtZXMuYXJnPXNlcSgxLDI5KSwgY29sLmxhYiA9IDAuNSkKZ3JpZCgpCmJhcnBsb3QoY29sTWVhbnMoTTJbLDI2OjU0XSwgbmEucm0gPSBGQUxTRSwgZGltcyA9IDEpLCBjb2w9cmFpbmJvdygzMiksIHhsaW09YygtMSwxKSwgbGFzPTIsIG1haW49IitEaWZmdXNpb24iLCBob3Jpej1UUlVFLCBuYW1lcy5hcmc9c2VxKDEsMjkpKQpncmlkKCkKYmFycGxvdChjb2xNZWFucyhNM1ssMjY6NTRdLCBuYS5ybSA9IEZBTFNFLCBkaW1zID0gMSksIGNvbD1yYWluYm93KDMyKSwgeGxpbT1jKC0xLDEpLCBsYXM9MiwgbWFpbj0iK1Rha2VvdmVyIiwgaG9yaXo9VFJVRSwgbmFtZXMuYXJnPXNlcSgxLDI5KSkKZ3JpZCgpCmJhcnBsb3QoY29sTWVhbnMoTTRbLDI2OjU0XSwgbmEucm0gPSBGQUxTRSwgZGltcyA9IDEpLCBjb2w9cmFpbmJvdygzMiksIHhsaW09YygtMSwxKSwgbGFzPTIsIG1haW49IitEaWZmdXNpb24gXG4gK1Rha2VvdmVyIiwgaG9yaXo9VFJVRSwgbmFtZXMuYXJnPXNlcSgxLDI5KSkKZ3JpZCgpCmRldi5vZmYoKQoKCmBgYAohW10oc2NhbGVkIHN0YXRzIHBlciBtb2RlbC5wbmcpCgoKCgoKCmBgYHtyIGV2YWw9RkFMU0V9CmxvYWQoIkZvdXJfbW9kZWxfY29tcGFyZV9yZXN1bHRzXzAyX0F1Z18yMDE3X2Nyb3BfdG9fNjEyOC5SZGF0YSIpCmV4dGFudCA8LSBDb25jYXRlbmF0ZWRfZGF0YQpleHRhbnQKCiBsb2FkKCJGb3VyX21vZGVsX2NvbXBhcmVfcmVzdWx0c19leHRpbmN0XzAyX0F1Z18yMDE3LlJkYXRhIikKZXh0aW5jdCA8LSBDb25jYXRlbmF0ZWRfZGF0YQpleHRpbmN0CgpoZWFkKGV4dGFudCkKaGVhZChleHRpbmN0KQpgYGAKCgoKCgoKYGBge3IgZXZhbD1GQUxTRX0KCmZvcihpIGluIGMoMzoyMikpewoJZXh0aW5jdFt3aGljaChpcy5uYW4oYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0aW5jdFssIGldKSkpID09IFRSVUUpLCBpXSA8LSBOQQp9Cgpmb3IoaSBpbiBjKDM6MjIpKXsKCWV4dGFudFt3aGljaChpcy5uYW4oYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0YW50WywgaV0pKSkgPT0gVFJVRSksIGldIDwtIE5BCn0KCgoKCnhsaW1pdCA8LSBjKDAsMSkKeWxpbWl0IDwtIGMoMCwzMDAwKQptYWluY2V4IDwtIDAuOQoKcG5nKGZpbGU9Ikdsb2JhbF9zdWNjZXNzX3JhdGVfcGVyX3BhcmFtZXRlci5wbmciLCB3aWR0aD04LjUsIGhlaWdodD0xMSwgdW5pdHM9ImluIiwgcmVzPTMwMCkKCnBhcihtZnJvdz1jKDUsNCksIG1hcj1jKDMsMywzLDApKQoKCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0aW5jdFssM10pKSwgbWFpbj0ic3BlY2lhdGlvbiBvZiBGIGluIEYgZW52IiwgY29sPWFkanVzdGNvbG9yKCJmaXJlYnJpY2siLCBhbHBoYT0wLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCkKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRhbnRbLDNdKSksIG1haW49InNwZWNpYXRpb24gb2YgRiBpbiBGIGVudiIsIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0wLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCwgYWRkPVRSVUUpCgoKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRpbmN0Wyw0XSkpLCBtYWluPSJzcGVjaWF0aW9uIG9mIEQgaW4gRiBlbnYiLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCkKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRhbnRbLDRdKSksIG1haW49InNwZWNpYXRpb24gb2YgRCBpbiBGIGVudiIsIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgsIGFkZD1UUlVFKQoKCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0aW5jdFssNV0pKSwgbWFpbj0ic3BlY2lhdGlvbiBvZiBGIGluIEQgZW52IiwgY29sPWFkanVzdGNvbG9yKCJmaXJlYnJpY2siLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgpCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0YW50Wyw1XSkpLCBtYWluPSJzcGVjaWF0aW9uIG9mIEYgaW4gRCBlbnYiLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4LCBhZGQ9VFJVRSkKCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0aW5jdFssNl0pKSwgbWFpbj0ic3BlY2lhdGlvbiBvZiBEIGluIEQgZW52IiwgY29sPWFkanVzdGNvbG9yKCJmaXJlYnJpY2siLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgpCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0YW50Wyw2XSkpLCBtYWluPSJzcGVjaWF0aW9uIG9mIEQgaW4gRCBlbnYiLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4LCBhZGQ9VFJVRSkKCiMjIyMjIyMKCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0aW5jdFssIDddKSksIG1haW49ImV4dGluY3Rpb24gb2YgRiBpbiBGIGVudiIsIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4KQpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGFudFssIDddKSksIG1haW49ImV4dGluY3Rpb24gb2YgRiBpbiBGIGVudiIsIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgsIGFkZD1UUlVFKQoKCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0aW5jdFssIDhdKSksIG1haW49ImV4dGluY3Rpb24gb2YgRCBpbiBGIGVudiIsIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4KQpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGFudFssIDhdKSksIG1haW49ImV4dGluY3Rpb24gb2YgRCBpbiBGIGVudiIsIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgsIGFkZD1UUlVFKQoKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRpbmN0WywgOV0pKSwgbWFpbj0iZXh0aW5jdGlvbiBvZiBGIGluIEQgZW52IiwgY29sPWFkanVzdGNvbG9yKCJmaXJlYnJpY2siLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgpCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0YW50WywgOV0pKSwgbWFpbj0iZXh0aW5jdGlvbiBvZiBGIGluIEQgZW52IiwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCwgYWRkPVRSVUUpCgpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGluY3RbLCAxMF0pKSwgbWFpbj0iZXh0aW5jdGlvbiBvZiBEIGluIEQgZW52IiwgY29sPWFkanVzdGNvbG9yKCJmaXJlYnJpY2siLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgpCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0YW50WywgMTBdKSksIG1haW49ImV4dGluY3Rpb24gb2YgRCBpbiBEIGVudiIsIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgsIGFkZD1UUlVFKQoKIyMjIyMjCgpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGluY3RbLCAxMV0pKSwgbWFpbj0iYXJpc2FsIG9mIEYgaW4gRiBlbnYiLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCkKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRhbnRbLCAxMV0pKSwgbWFpbj0iYXJpc2FsIG9mIEYgaW4gRiBlbnYiLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4LCBhZGQ9VFJVRSkKCgoKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRpbmN0WywgMTJdKSksIG1haW49ImFyaXNhbCBvZiBEIGluIEYgZW52IiwgY29sPWFkanVzdGNvbG9yKCJmaXJlYnJpY2siLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgpCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0YW50WywgMTJdKSksIG1haW49ImFyaXNhbCBvZiBEIGluIEYgZW52IiwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCwgYWRkPVRSVUUpCgpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGluY3RbLCAxM10pKSwgbWFpbj0iYXJpc2FsIG9mIEYgaW4gRCBlbnYiLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCkKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRhbnRbLCAxM10pKSwgbWFpbj0iYXJpc2FsIG9mIEYgaW4gRCBlbnYiLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4LCBhZGQ9VFJVRSkKCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0aW5jdFssIDE0XSkpLCBtYWluPSJhcmlzYWwgb2YgRCBpbiBEIGVudiIsIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4KQpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGFudFssIDE0XSkpLCBtYWluPSJhcmlzYWwgb2YgRCBpbiBEIGVudiIsIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgsIGFkZD1UUlVFKQoKIyMjIyMjCgpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGluY3RbLCAxNV0pKSwgbWFpbj0iTk9QRSAtLSBEaWZmdXNpb246IHNvdXJjZSBGLCB0YXJnZXQgRiIsIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSBjKDAsMTgwMDApLCBjZXgubWFpbj0gbWFpbmNleCkKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRhbnRbLCAxNV0pKSwgbWFpbj0iTk9QRSAtLSBEaWZmdXNpb246IHNvdXJjZSBGLCB0YXJnZXQgRiIsIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IGMoMCwxODAwMCksIGNleC5tYWluPSBtYWluY2V4LCBhZGQ9VFJVRSkKCgoKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRpbmN0WywgMTZdKSksIG1haW49IkRpZmZ1c2lvbjogc291cmNlIEQsIHRhcmdldCBGIiwgY29sPWFkanVzdGNvbG9yKCJmaXJlYnJpY2siLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgpCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0YW50WywgMTZdKSksIG1haW49IkRpZmZ1c2lvbjogc291cmNlIEQsIHRhcmdldCBGIiwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCwgYWRkPVRSVUUpCgpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGluY3RbLCAxN10pKSwgbWFpbj0iRGlmZnVzaW9uOiBzb3VyY2UgRiwgdGFyZ2V0IEQiLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCkKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRhbnRbLCAxN10pKSwgbWFpbj0iRGlmZnVzaW9uOiBzb3VyY2UgRiwgdGFyZ2V0IEQiLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4LCBhZGQ9VFJVRSkKCmhpc3QoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZXh0aW5jdFssIDE4XSkpLCBtYWluPSJOT1BFIC0tIERpZmZ1c2lvbjogc291cmNlIEQsIHRhcmdldCBEIiwgY29sPWFkanVzdGNvbG9yKCJmaXJlYnJpY2siLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IGMoMCwxODAwMCksIGNleC5tYWluPSBtYWluY2V4KQpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGFudFssIDE4XSkpLCBtYWluPSJOT1BFIC0tIERpZmZ1c2lvbjogc291cmNlIEQsIHRhcmdldCBEIiwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0gYygwLDE4MDAwKSwgY2V4Lm1haW49IG1haW5jZXgsIGFkZD1UUlVFKQoKIyMjIwoKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRpbmN0WywgMTldKSksIG1haW49IlRha2VvdmVyOiBzb3VyY2UgRiwgdGFyZ2V0IEYiLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCkKaGlzdChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihleHRhbnRbLCAxOV0pKSwgbWFpbj0iVGFrZW92ZXI6IHNvdXJjZSBGLCB0YXJnZXQgRiIsIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0gMC43KSwgYnJlYWtzPTEwMCwgYm9yZGVyPU5BLCB4bGltPSB4bGltaXQsIHlsaW09IHlsaW1pdCwgY2V4Lm1haW49IG1haW5jZXgsIGFkZD1UUlVFKQoKCgpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGluY3RbLCAyMF0pKSwgbWFpbj0iVGFrZW92ZXI6IHNvdXJjZSBELCB0YXJnZXQgRiIsIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4KQpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGFudFssIDIwXSkpLCBtYWluPSJUYWtlb3Zlcjogc291cmNlIEQsIHRhcmdldCBGIiwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCwgYWRkPVRSVUUpCgpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGluY3RbLCAyMV0pKSwgbWFpbj0iVGFrZW92ZXI6IHNvdXJjZSBGLCB0YXJnZXQgRCIsIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4KQpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGFudFssIDIxXSkpLCBtYWluPSJUYWtlb3Zlcjogc291cmNlIEYsIHRhcmdldCBEIiwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCwgYWRkPVRSVUUpCgpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGluY3RbLCAyMl0pKSwgbWFpbj0iVGFrZW92ZXI6IHNvdXJjZSBELCB0YXJnZXQgRCIsIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuNyksIGJyZWFrcz0xMDAsIGJvcmRlcj1OQSwgeGxpbT0geGxpbWl0LCB5bGltPSB5bGltaXQsIGNleC5tYWluPSBtYWluY2V4KQpoaXN0KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGV4dGFudFssIDIyXSkpLCBtYWluPSJUYWtlb3Zlcjogc291cmNlIEQsIHRhcmdldCBEIiwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPSAwLjcpLCBicmVha3M9MTAwLCBib3JkZXI9TkEsIHhsaW09IHhsaW1pdCwgeWxpbT0geWxpbWl0LCBjZXgubWFpbj0gbWFpbmNleCwgYWRkPVRSVUUpCgoKZGV2Lm9mZigpCgoKCgoKCmBgYAoKCiFbXShHbG9iYWxfc3VjY2Vzc19yYXRlX3Blcl9wYXJhbWV0ZXIucG5nKQoKIyMgUmFuZG9tIEZvcmVzdCBBbmFseXNpcwoKIyMjIFRyYWluaW5nL0J1aWxkaW5nCiAgVGhlIHByb2NlZHVyZXMgZGVzY3JpYmVkIHNvIGZhciBoYXZlIG91dGxpbmVkIHRoZSBjcmVhdGlvbiBvZiBhIGRhdGFzZXQgdGhhdCBpbmNsdWRlcyB0aGUgaW5wdXQgYW5kIG91dHB1dCB2YXJpYWJsZXMgZm9yIGVhY2ggcmVwbGljYXRlIHNpbXVsYXRpb24uIFdoZW4gdGFrZW4gYXMgYSB3aG9sZSwgdGhhdCBkYXRhc2V0IGRlc2NyaWJlcyBhIGRpc3RyaWJ1dGlvbiBvZiBwb3NzaWJsZSBudW1lcmljYWwgb3V0Y29tZXMgdGhhdCBhcmUgcG9zc2libGUgZ2l2ZW4gdGhlIDQgYXZhaWxhYmxlIGlucHV0IHR5cGVzLiBUaGUgcHVycG9zZSBvZiB0aGUgcmFuZG9tIGZvcmVzdCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobSAoQnJlaW1hbiAyMDAxLCBMaWF3IGFuZCBXaWVuZXIgMjAxNSkgd2FzIHRvIGNvcnJlbGF0ZSBpbnB1dCBhbmQgb3V0cHV0IHZhcmlhYmxlcyBhcyBhIG1lYW5zIG9mIGNhdGVnb3JpemluZyBzaW11bGF0aW9uIG91dHB1dHMgYWNjb3JkaW5nIHRvIHRoZSB0eXBlIG9mIGlucHV0IHRoYXQgaGFkIGNyZWF0ZWQgdGhlbS4gV2UgdXNlZCA3MCUgb2Ygb3VyIGF2YWlsYWJsZSBzaW11bGF0aW9uIGRhdGEgdG8gYnVpbGQgdGhlIHJhbmRvbSBmb3Jlc3QsIHRoZSByZW1haW5pbmcgMzAlIHRvIHRlc3QgdGhlIGZvcmVzdCdzIGFjY3VyYWN5IGFuZCBwcmVjaXNpb24gYXQgaW5mZXJyaW5nIGlucHV0IGNhdGVnb3JpZXMsIGFuZCB0aGVuIHVzZWQgdGhhdCB0cmFpbmVkIGFuZCB2ZXR0ZWQgYWxnb3JpdGhtIHRvIGluZmVyIHRoZSBtb3N0IGxpa2VseSBpbnB1dCBjYXRlZ29yeSBmb3IgcmVhbCB3b3JsZCBodW1hbiBvdXRwdXQgZGF0YS4KCiAgVGhpcyByYW5kb20gZm9yZXN0IG1vZGVsIHdhcyB0cmFpbmVkIGJ5IGJ1aWxkaW5nIGEgZm9yZXN0IG9mIGRlY2lzaW9uIHRyZWVzLiBFYWNoIGRlY2lzaW9uIHRyZWUgZGVzY3JpYmVzIGEgc2VyaWVzIG9mIG9yZGVyZWQgZGljaG90b21vdXMgZGVjaXNpb25zIHRoYXQgY2F0ZWdvcml6ZSB0aGUgMTIgb3V0cHV0IHN0YXRpc3RpY3MgaW50byBvbmUgb2YgNCBpbnB1dCB0eXBlcy4gSW5kaXZpZHVhbCB0cmVlcyB3ZXJlIGJ1aWx0IGJ5IGZpcnN0IHRha2luZyBhIHJhbmRvbWx5IHNpemVkIGFuZCByYW5kb21seSBzZWxlY3RlZCBzYW1wbGUgb2Ygcm93cyAocmVwbGljYXRlIHNpbXVsYXRpb25zKSBhbmQgY29sdW1ucyAob3V0cHV0IHZhcmlhYmxlcykgZnJvbSB0aGUgZnVsbCBzaW11bGF0aW9uIGRhdGFzZXQgdG8gY3JlYXRlIGEgdW5pcXVlIHN1YnNldCBkYXRhc2V0LiBUaGF0IHVuaXF1ZSBkYXRhc2V0IHdhcyB0aGVuIHN1YmplY3RlZCB0byBhIGJhZ2dpbmcgbW9kZWwgdG8gcmFuayB0aGUgdmFyaWFibGVzIGluIG9yZGVyIG9mIGltcG9ydGFuY2UgYW5kIGJ1aWxkIGEgZGVjaXNpb24gdHJlZSBvZmYgb2YgdGhhdCByYW5rLiBXZSByZXBlYXRlZCB0aGlzIHByb2Nlc3MsIHdpdGggcmVwbGFjZW1lbnQsIHRvIGJ1aWxkIDMwMDAgaW5kaXZpZHVhbCBkZWNpc2lvbiB0cmVlcyBzYW1wbGVkIGZyb20gNzAlIChuID0gNDAwMDAgeCAwLjcgPSAyODAwMCkgb2YgdGhlIHRvdGFsIHNpbXVsYXRpb24gZGF0YS4gCgojIyMgVGVzdGluZwogIFRoZSByYW5kb20gZm9yZXN0IHdhcyB2ZXR0ZWQgdXNpbmcgdGhlIHJlbWFpbmluZyAzMCUgb2YgdGhlIHNpbXVsYXRpb24gZGF0YSByZXNlcnZlZCBmb3IgbW9kZWwgdGVzdGluZy4gUmF0aGVyIHRoYW4gYnVpbGRpbmcgZGVjaXNpb24gdHJlZXMgd2l0aCB0aGVzZSBkYXRhLCB3ZSBmZWQgZWFjaCByZXBsaWNhdGUgdGhyb3VnaCB0aGUgcmVjZW50bHkgYnVpbHQgZm9yZXN0IHNvIGl0IGNvdWxkIGNsYXNzaWZ5IG91dHB1dHMgYW5kIHRoZW4gY29tcGFyZSB0aG9zZSBpbmZlcnJlZCBpbnB1dCB0eXBlcyB0byB0aGUga25vd24gaW5wdXQgdHlwZXMgdG8gdGhlIHRvIHNlZSBob3cgZnJlcXVlbnRseSB0aGUgZm9yZXN0IHJldHVybmVkIHRoZSBjb3JyZWN0IGNsYXNzaWZpY2F0aW9uLiBUaGUgYWNjdXJhY3kgYW5kIHByZWNpc2lvbiBvZiB0aGVzZSB0ZXN0cyBhcmUgcmVwb3J0ZWQgaW4gYSBjb25mdXNpb24gbWF0cml4IHNob3dpbmcgdGhlIG51bWJlciBvZiBtYXRjaGVzIHRoYXQgd2VyZSBjb3JyZWN0bHkgb3IgaW5jb3JyZWN0bHkgbWFkZSBiZXR3ZWVuIGlucHV0cyBhbmQgb3V0cHV0cyBmb3IgZWFjaCBtb2RlbCAuIApPdXIgdHJhaW5lZCByYW5kb20gZm9yZXN0IGFsZ29yaXRobSBjb3VsZCBjb3JyZWN0bHkgbWF0Y2ggb3V0cHV0IHN0YXRpc3RpY3Mgd2l0aCBpbnB1dCBtZWNoYW5pc21zIHdpdGggdmVyeSBnb29kIGFjY3VyYWN5IC4gSXQgaXMgbm90ZXdvcnRoeSB0aGF0IHRoZXJlIHdhcyB2ZXJ5IGxpdHRsZSBjb25mdXNpb24gYmV0d2VlbiB0aGUgdHdvIGNvbXBldGluZyBoeXBvdGhlc2l6ZWQgbWVjaGFuaXNtcywgZGlmZnVzaW9uIGFuZCBkaWZmdXNpb24gYnkgdGFrZW92ZXIuIElmIHRoZSB0cmFpbmVkIHJhbmRvbSBmb3Jlc3QgYWxnb3JpdGhtIGlzIHByZXNlbnRlZCB3aXRoIGEgc2ltdWxhdGlvbiBjcmVhdGVkIGJ5IGRpZmZ1c2lvbiwgaWYgaXMgdmVyeSB1bmxpa2VseSB0byBiZSBjbGFzc2lmeSBpdCBhcyBjb21pbmcgZnJvbSBkaWZmdXNpb24gYnkgdGFrZW92ZXIuIAoKIyMjIE1hdGNoaW5nCiAgVGhlIHN0cnVjdHVyZSBvZiBvdXIgc2ltdWxhdGlvbiBvdXRwdXRzIHdoZXJlIGRlc2lnbmVkIHRvIG1pbWljIHRoZSBzdHJ1Y3R1cmUgb2Ygb3VyIHJlYWwgY3VsdHVyYWwgZGF0YSBzbyB0aGF0IHRoZSB0d28gY291bGQgYmUgY29tcGFyZWQgZGlyZWN0bHkuIEluIHRoZSBzYW1lIHdheSB0aGUgb3V0cHV0IGZvciByZXBsaWNhdGUgc2ltdWxhdGlvbnMgd2hlcmUgcHJvY2Vzc2VkLCB0aGUgcGh5bG9nZW55IGFuZCBzcGF0aWFsIHBhdHRlcm4gb2YgZXh0YW50IGh1bWFuIGN1bHR1cmVzIHdlcmUgc3VtbWFyaXplZCB1c2luZyAxMiBzdW1tYXJ5IHN0YXRpc3RpY3MgcGFzc2VkIHRvIHRoZSB0cmFpbmVkIHJhbmRvbSBmb3Jlc3QgYWxnb3JpdGhtLiBUaGUgcmFuZG9tIGZvcmVzdCBhc3NpZ25lZCB0aGUgbW9zdCBsaWtlbHkgbWVjaGFuaXNtIHRvIGhhdmUgY2F1c2VkIHRoYXQgaHVtYW4ncyBrbm93biBoaXN0b3JpY2FsIHRyYWplY3RvcnkuIEl0IGRvZXMgdGhpcyBieSBhc2tpbmcgZXZlcnkgZGljaG90b21vdXMgZGVjaXNpb24gdHJlZSBpbiB0aGUgcmFuZG9tIGZvcmVzdCB0byB2b3RlIG9uIHdoaWNoIGh5cG90aGVzaXplZCBtZWNoYW5pc20gY3JlYXRlZCB0aGUgb3V0cHV0IHByb3ZpZGVkIGJ5IHRoZSBkYXRhIGFuZCB0aGVuIHRhbGx5aW5nIHRob3NlIHZvdGVzIGludG8gYSBjb25zZW5zdXMgdm90ZS4gT3VyIGNvbnNlbnN1cyB2b3RlIHNob3dlZCB0aGF0IG92ZXIgNTAlIG9mIHRoZSB0cmVlcyBiZWxpZXZlIHRoYXQgdGhlIGN1cnJlbnQgY29uZmlndXJhdGlvbiBvZiBhZ3JpY3VsdHVyZSBhY3Jvc3MgdGhlIGdsb2JlIGlzIHRoZSByZXN1bHQgb2YgZGVtaWMgZGlmZnVzaW9uIGF1Z21lbnRlZCBieSBjdWx0dXJhbCBkaWZmdXNpb24uIEFib3V0IDM1JSBvZiB0aGUgdHJlZXMgYXNzaWduZWQga25vd24gZ2xvYmFsIHRyZW5kIHRvIHRoZSBiYXNpYyBtb2RlbCB0aGF0IGluY2x1ZGVzIG9ubHkgZGVtaWMgZGlmZnVzaW9uLCB3aGlsZSB0aGUgb3RoZXIgdHdvIG1vZGVscyByZWNlaXZlZCBhbG1vc3Qgbm8gdm90ZXMuIFRoaXMgc3VnZ2VzdCB0aGF0IGlmIHdlIGFyZSB3cm9uZyB0aGF0IGRlbWljIGFuZCBjdWx0dXJhbCBkaWZmdXNpb24gY29udHJpYnV0ZWQgdG8gY3VycmVudCBkaXN0cmlidXRpb25zIG9mIGFncmljdWx0dXJpc3QgY3VsdHVyZXMsIHRoZSBuZXh0IG1vc3QgbGlrZWx5IGFuc3dlciBpcyB0aGF0IGl0IGRpZmZ1c2lvbiBjcmVhdGVkIHRoZXNlIHBhdHRlcm5zIHdpdGggbm8gaGVscCBmcm9tIGN1bHR1cmFsIGRpZmZ1c2lvbi4KCgojIyBSdW4gYSBzaW5nbGUgcmFuZG9tIGZvcmVzdCBvbiBhdmFpbGFibGUgb3V0cHV0cwpgYGB7ciwgZXZhbD1GQUxTRX0KIyBQYWNrYWdlcwpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKCiMgUmFkb20gRm9yZXN0IEZ1bmN0aW9uClJGIDwtIGZ1bmN0aW9uKHRhYmxlX3NpbSwgdGFibGVfcmVhbCwgbnRyZWUgPSAyMDAwLCBzdGF0c19yZW1vdmUgPSBOVUxMLAogICAgICAgICAgICAgIHJlcGV0aXRpb25zID0gMSkgewogIyB0YWJsZV9zaW0gPSB0YWJsZSBjcm9wZWQgCgogYmVnaW4gPC0gd2hpY2goY29sbmFtZXModGFibGVfc2ltKSA9PSAgIm51bWJlcl9vZl9icmFuY2hlcyIpCiB0YWJsZV9zaW1fZGF0YSA8LSB0YWJsZV9zaW1bLCBiZWdpbjpuY29sKHRhYmxlX3NpbSldCiB0YWJsZV9zaW1fZGF0YSA8LSBhcHBseSh0YWJsZV9zaW1fZGF0YSwgMiwgYXMubnVtZXJpYykKIGlmIChhbnkoaXMuaW5maW5pdGUodGFibGVfc2ltX2RhdGEpKSkgewogICBzdG9wKCJUaGVyZSBpcyBpbmZpbml0ZSB2YWx1ZXMgaW4gdGhlIHNpbXVsYXRpb24gc3RhdGlzdGljcyIpCiB9CiByZl9kYXRhIDwtIGRhdGEuZnJhbWUoIk1vZGVsIiA9IHRhYmxlX3NpbSRjb21ibywgdGFibGVfc2ltX2RhdGEpCiByZl9kYXRhX3JlYWwgPC0gZGF0YS5mcmFtZSh0YWJsZV9yZWFsKQoKIGlmICghaXMubnVsbChzdGF0c19yZW1vdmUpKSB7CiAgIHJmX2RhdGEgPC0gcmZfZGF0YVssIC1zdGF0c19yZW1vdmVdCiAgIHJmX2RhdGFfcmVhbCA8LSByZl9kYXRhX3JlYWxbLCAtc3RhdHNfcmVtb3ZlXQogfQoKIGZ1biA8LSBmdW5jdGlvbih4LCB5LCBwZXIgPSAuMzMpIHsKICAgc2FtcGxlKHdoaWNoKHkkTW9kZWwgPT0geCksIHJvdW5kKHRhYmxlKHkkTW9kZWwpWzFdKnBlcikpCiB9CgogcmVzdWx0cyA8LSBtYXRyaXgobnJvdyA9IHJlcGV0aXRpb25zLCBuY29sID0gbGVuZ3RoKHRhYmxlX3JlYWwpICogNiArIDIxKQogZm9yIChpIGluIDE6cmVwZXRpdGlvbnMpIHsKICAgc3ViLnRlc3QgPC0gdW5saXN0KGxhcHBseShhcy5saXN0KHBhc3RlMCgwLCBjKDEsMiw1LDYpKSksIGZ1biwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcmZfZGF0YSkpCiAgIHRlc3QyIDwtIHJmX2RhdGFbc3ViLnRlc3QsIDI6bmNvbChyZl9kYXRhKV0KICAgdGVzdDEgPC0gcmZfZGF0YVtzdWIudGVzdCwgMV0KICAgdHJhaW4gPC0gcmZfZGF0YVstc3ViLnRlc3QsIF0KCiAgIGZpdCA8LSByYW5kb21Gb3Jlc3QoTW9kZWwgfiAuLCBkYXRhID0gdHJhaW4sIHh0ZXN0ID0gdGVzdDIsIAogICAgICAgICAgICAgICAgICAgICAgIHl0ZXN0ID0gdGVzdDEsIGltcG9ydGFuY2UgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IG50cmVlLCBrZWVwLmZvcmVzdCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZSA9IFRSVUUpCiAgI2Rldi5uZXcoKQogICNwbG90KGZpdCkKICAKICAgcHJlZGljdGlvbnMgPC0gcHJlZGljdChmaXQsIHJmX2RhdGFfcmVhbCwgdHlwZSA9ICJwcm9iIikKICAKICAgdmFyX2ltcG9ydCA8LSBpbXBvcnRhbmNlKGZpdCkKCiAgIGVycm9yIDwtIG1lYW4oZml0JHRlc3QkY29uZnVzaW9uWywgNV0pCiAgIGNvbmZ1c2lvbiA8LSBhcy5udW1lcmljKGZpdCR0ZXN0JGNvbmZ1c2lvblssIDE6NF0pCiAgIG5hbWVzKGNvbmZ1c2lvbikgPC0gcGFzdGUwKCJDb25mdXNpb25fIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcChjb2xuYW1lcyhmaXQkdGVzdCRjb25mdXNpb25bLCAxOjRdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVhY2ggPSA0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXMoZml0JHRlc3QkY29uZnVzaW9uWywgMTo0XSkpCiAgIHByZWRpY3Rpb25zX3Byb2IgPC0gYXMubnVtZXJpYyhwcmVkaWN0aW9ucykKICAgbmFtZXMocHJlZGljdGlvbnNfcHJvYikgPC0gcGFzdGUwKCJQcmVkaWN0aW9uX3Byb2IiLCBjb2xuYW1lcyhwcmVkaWN0aW9ucykpCiAgIHZhcl9pbXBvcnRfdmVjIDwtIGFzLm51bWVyaWModmFyX2ltcG9ydCkKICAgbmFtZXModmFyX2ltcG9ydF92ZWMpIDwtIHBhc3RlMCgidmFyX2ltcF8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoY29sbmFtZXModmFyX2ltcG9ydCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlYWNoID0gbnJvdyh2YXJfaW1wb3J0KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIl8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzKHZhcl9pbXBvcnQpKQogICB2ZWMgPC0gYyhlcnJvciwgY29uZnVzaW9uLCBwcmVkaWN0aW9uc19wcm9iLCB2YXJfaW1wb3J0X3ZlYykKICAgCiAgIHJlc3VsdHNbaSwgXSA8LSB2ZWMKIH0KIGNvbG5hbWVzKHJlc3VsdHMpIDwtIG5hbWVzKHZlYykKIGNvbG5hbWVzKHJlc3VsdHMpWzFdIDwtICJFcnJvcl90ZXN0IgogcmVzdWx0cyA8LSBjYmluZCgxOm5yb3cocmVzdWx0cyksIHJlc3VsdHMpCiBjb2xuYW1lcyhyZXN1bHRzKVsxXSA8LSAiUmVwbGljYXRlIgogcmV0dXJuKHJlc3VsdHMpCn0KCiMgTXVsdGlwbGUgUmFuZG9tIEZvcmVzdApSRl9tdWx0IDwtIGZ1bmN0aW9uKHRhYmxlX3NpbSwgdGFibGVfcmVhbF9tdWx0LCBudHJlZSA9IDIwMDAsIAogICAgICAgICAgICAgICAgICAgc3RhdHNfcmVtb3ZlID0gTlVMTCwgcmVwZXRpdGlvbnMgPSAxKSB7CiBuX3IgPC0gbnJvdyh0YWJsZV9yZWFsX211bHQpCiBuIDwtIHJlcGV0aXRpb25zICogbl9yCiBuX2NvbCA8LSBuY29sKHRhYmxlX3JlYWxfbXVsdCkgKiA2ICsgMjIKIHJlc3VsdHMgPC0gbWF0cml4KG5yb3cgPSBuLCBuY29sID0gbl9jb2wpCiB4IDwtIDAKIGZvciAoaSBpbiBzZXEoMSwgbiwgcmVwZXRpdGlvbnMpKSB7CiAgIHByaW50KHBhc3RlMCgicmFuZG9tRm9yZXN0IHN0YXJ0IGZvciB0cmVlIiwgaSkpCiAgIHggPC0geCArIDEKICAgdGVtcCA8LSBSRih0YWJsZV9zaW0sIAogICAgICAgICAgICAgIHRhYmxlX3JlYWxfbXVsdFt4LCAsIGRyb3AgPSBGQUxTRV0sCiAgICAgICAgICAgICAgbnRyZWUgPSBudHJlZSwgCiAgICAgICAgICAgICAgcmVwZXRpdGlvbnMgPSByZXBldGl0aW9ucywgCiAgICAgICAgICAgICAgc3RhdHNfcmVtb3ZlID0gc3RhdHNfcmVtb3ZlKQogICByZXN1bHRzW2k6KGkgKyByZXBldGl0aW9ucyAtIDEpLCBdIDwtIHRlbXAKIH0KIHRyZWUgPC0gcmVwKDE6bl9yLCBlYWNoID0gcmVwZXRpdGlvbnMpCiByZXN1bHRzIDwtIGNiaW5kKHRyZWUsIHJlc3VsdHMpCiBjb2xuYW1lcyhyZXN1bHRzKSA8LSBjKCJUcmVlIiwgY29sbmFtZXModGVtcCkpCiByZXR1cm4ocmVzdWx0cykKfQoKIyBBY2N1bXVsYXRpb24gZnVuY3Rpb24KUkZfYWN1bSA8LSBmdW5jdGlvbih0YWJsZV9zaW0sIHRhYmxlX3JlYWxfbXVsdCwgbnRyZWUgPSAyMDAwLCAKICAgICAgICAgICAgICAgICAgIHN0YXRzX3JlbW92ZSA9IE5VTEwsIHJlcGV0aXRpb25zID0gMSwKICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24gPSAxMDAsIG1pbmltdW4gPSAxMDApIHsKIHNlcXVlbmNlIDwtIHNlcShtaW5pbXVuLCBucm93KHRhYmxlX3NpbSksIHJlc29sdXRpb24pWzJdCiBuX2NvbCA8LSBuY29sKHRhYmxlX3JlYWxfbXVsdCkgKiA2ICsgMjQgCiBuX3IgPC0gbnJvdyh0YWJsZV9yZWFsX211bHQpCiBuX3NlcSA8LSBsZW5ndGgoc2VxdWVuY2UpCiBuIDwtIHJlcGV0aXRpb25zICogbl9yICogbl9zZXEKIHJlc3VsdHMgPC0gbWF0cml4KG5yb3cgPSBuLCBuY29sID0gbl9jb2wpCiBsb3R0ZXJ5MSA8LSB3aGljaCh0YWJsZV9zaW0kY29tYm8gPT0gIjAxIikKIGxvdHRlcnkyIDwtIHdoaWNoKHRhYmxlX3NpbSRjb21ibyA9PSAiMDIiKQogbG90dGVyeTUgPC0gd2hpY2godGFibGVfc2ltJGNvbWJvID09ICIwNSIpCiBsb3R0ZXJ5NiA8LSB3aGljaCh0YWJsZV9zaW0kY29tYm8gPT0gIjA2IikKIHN1YjEgPC0gc2FtcGxlKGxvdHRlcnkxLCBzZXF1ZW5jZVsxXS80LCByZXBsYWNlID0gRkFMU0UpCiBzdWIyIDwtIHNhbXBsZShsb3R0ZXJ5Miwgc2VxdWVuY2VbMV0vNCwgcmVwbGFjZSA9IEZBTFNFKQogc3ViNSA8LSBzYW1wbGUobG90dGVyeTUsIHNlcXVlbmNlWzFdLzQsIHJlcGxhY2UgPSBGQUxTRSkKIHN1YjYgPC0gc2FtcGxlKGxvdHRlcnk2LCBzZXF1ZW5jZVsxXS80LCByZXBsYWNlID0gRkFMU0UpCiBmb3IgKGkgaW4gMTpuX3NlcSkgewogICBpZiAoaSAhPSAxKSB7CiAgICAgc3ViMSA8LSBjKHN1YjEsIHNhbXBsZShsb3R0ZXJ5MVstc3ViMV0sIHNlcXVlbmNlWzFdLzQsIHJlcGxhY2UgPSBGQUxTRSkpCiAgICAgc3ViMiA8LSBjKHN1YjIsIHNhbXBsZShsb3R0ZXJ5Mlstc3ViMl0sIHNlcXVlbmNlWzFdLzQsIHJlcGxhY2UgPSBGQUxTRSkpCiAgICAgc3ViNSA8LSBjKHN1YjUsIHNhbXBsZShsb3R0ZXJ5NVstc3ViNV0sIHNlcXVlbmNlWzFdLzQsIHJlcGxhY2UgPSBGQUxTRSkpCiAgICAgc3ViNiA8LSBjKHN1YjYsIHNhbXBsZShsb3R0ZXJ5Nlstc3ViNl0sIHNlcXVlbmNlWzFdLzQsIHJlcGxhY2UgPSBGQUxTRSkpCiAgIH0KICBwcmludChwYXN0ZTAoIk1VTFRJIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBSRiAtLSAiLCBpKSkKICAgdGVtcCA8LSBSRl9tdWx0KHRhYmxlX3NpbVtjKHN1YjEsIHN1YjIsIHN1YjUsIHN1YjYpLCBdLCB0YWJsZV9yZWFsX211bHQsCiAgICAgICAgICAgICAgICAgICBudHJlZSA9IG50cmVlLCByZXBldGl0aW9ucyA9IHJlcGV0aXRpb25zLAogICAgICAgICAgICAgICAgICAgc3RhdHNfcmVtb3ZlID0gc3RhdHNfcmVtb3ZlKQogICBuX3RlbXAgPC0gbnJvdyh0ZW1wKQogICByZXN1bHRzWzE6bl90ZW1wICsgKG5fdGVtcCAqIChpIC0gMSkpLCBdIDwtIGNiaW5kKHJlcChzZXF1ZW5jZVtpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl90ZW1wKSwgdGVtcCkKIH0KIGNvbG5hbWVzKHJlc3VsdHMpIDwtIGMoIlN1YnNhbXBsZV9zaXplIiwgY29sbmFtZXModGVtcCkpCiByZXR1cm4ocmVzdWx0cykKfQoKIyBURVNUIEZJTkFMIChSVU4gT05MWSBUSElTKQojIExvYWQgZGF0YQpzZXR3ZCgifi9Ecm9wYm94L05hdHVyZV9GQVJNX3B1YmxpY2F0aW9uX3ByZXAvQ29kZSBmb3IgcHVibGljYXRpb24vUkYgYWZ0ZXIgYnJ1bm8iKQpsb2FkKCdGb3VyX21vZGVsX2NvbXBhcmVfcmVzdWx0c18yM19PY3RfMjAxN19jcm9wX3RvXzEwMzIzLlJkYXRhJykKbG9hZCgicmVhbC5hbmFseXNpc19tdWx0LlJEYXRhIikKCnRhYmxlX3NpbSA9IENvbmNhdGVuYXRlZF9kYXRhCnRhYmxlX3JlYWwgPSByZWFsLmFuYWx5c2lzLm11bHRbWzFdXVtbMV1dCnRhYmxlX3JlYWxfbXVsdCA9IGRvLmNhbGwocmJpbmQsIGxhcHBseShyZWFsLmFuYWx5c2lzLm11bHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KXt4W1sxXV19KSkKZGltKHRhYmxlX3JlYWxfbXVsdFssLGRyb3A9RkFMU0VdKQojc3RhcnQgdGltZQp0aW1lX3N0YXJ0IDwtIFN5cy50aW1lKCkKCiMgQ2hhbmdlIHRoZSBwYXJhbWV0ZXJzIGFzIHlvdSB3aXNoCiMjIHRhYmxlX3JlYWxfbXVsdCBpcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHBvc3NpYmxlIHRyZWVzIHByb3ZpZGVkIGJ5IE1QSQojIyB0YWJsZV9zaW0gaXMgb3VyIHNpbXVsYXRlZCBkYXRhCnJlc19hY3VtIDwtIFJGX2FjdW0odGFibGVfc2ltLCB0YWJsZV9yZWFsX211bHRbMToyMDAsLGRyb3A9RkFMU0VdLCBudHJlZSA9IDEwMDAwLCAKICAgICAgICAgICAgICAgICAgIHN0YXRzX3JlbW92ZSA9IE5VTEwsIHJlcGV0aXRpb25zID0gMSwKICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24gPSA1MDAwLCBtaW5pbXVuID0gOTUwMCkKICAgICAgICAgICAgICAgICAgIAojc3RvcCB0aW1lCnRpbWVfc3RvcCA8LSBTeXMudGltZSgpCmRpZmZ0aW1lKHRpbWVfc3RvcCwgdGltZV9zdGFydCkgICAgICAgICAgICAgICAgICAgCgpzYXZlKHJlc19hY3VtLCBmaWxlPSJSYW5kb21fRm9yZXN0X291dHB1dF9kYXRhX2Zvcl9wdWJsaWNhdGlvbl81X0ZlYnJ1YXJ5XzIwMTguUmRhdGEiKQoKCgpgYGAKCgojIyBWaXN1YWxpemUgb3V0cHV0cyBmcm9tIFJhbmRvbSBGb3Jlc3QgYW5hbHlzaXMKCmBgYHtyIGV2YWw9RkFMU0V9CiMgbG9hZCBvdXRwdXRzIGZyb20gUkYgcnVucwpsb2FkKCIvVXNlcnMvVHkvRGVza3RvcC9zbWFsbF9SRl9vdXQuUmRhdGEiKQphIDwtIHJlc19hY3VtCgpsb2FkKCIvVXNlcnMvVHkvRGVza3RvcC8xMF9tb3JlX3RyZWVfb3V0LlJkYXRhIikKYiA8LSByZXNfYWN1bQoKbG9hZCgiL1VzZXJzL1R5L0Rlc2t0b3AvMTBfdHJlZV9vdXQuUmRhdGEiKQpjIDwtIHJlc19hY3VtCgojb2JqZWN0IGNhbGxlZCByZXNfYWN1bQpyZXNfYWN1bSA8LSByYmluZChiLGMpCgpsb2FkKCJSYW5kb21fRm9yZXN0X291dHB1dF9kYXRhX2Zvcl9wdWJsaWNhdGlvbl8yX0ZlYnJ1YXJ5XzIwMTguUmRhdGEiKQoKYGBgCgoKYGBge3IsIGV2YWw9RkFMU0V9CnBuZygib3ZlcmFsbF9lcnJvcl9wZXJfc2FtcGxlX3NpemUucG5nIiwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gOC41LCByZXMgPSAzMDAsIHVuaXRzID0gImluIikKcGxvdChhZ2dyZWdhdGUoeCA9IHJlc19hY3VtWywgNF0sIGJ5ID0gbGlzdCgocmVzX2FjdW1bLCAxXSkpLCBGVU4gPSBtZWFuKSwgdHlwZT0ibCIsIHlsaW09YygwLDAuNSksIHlsYWI9Im92ZXJhbGwgY29uZnVzaW9uIGVycm9yIiwgeGxhYj0ic2FtcGxlIHNpemUiKQpkZXYub2ZmKCkKYGBgCiFbXShvdmVyYWxsX2Vycm9yX3Blcl9zYW1wbGVfc2l6ZS5wbmcpCgoKYGBge3IsIGV2YWw9RkFMU0V9CgoKcG5nKCJwcmVkaWN0aW9ucy5wbmciLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA4LjUsIHJlcyA9IDMwMCwgdW5pdHMgPSAiaW4iKQpwYXIobWFyPWMoNSw2LDIsMSkpCgojIENoYW5nZSBhcmd1bWVudCBGVU4gdG8gc2QgdG8gb2J0YWluIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24KcGxvdChhZ2dyZWdhdGUoeCA9IHJlc19hY3VtWywgNF0sIGJ5ID0gbGlzdCgocmVzX2FjdW1bLCAxXSkpLCBGVU4gPSBtZWFuKSwgdHlwZSA9ICJuIiwgeWxpbT1jKDAsMSksIHlsYWI9InByb2JhYmlsaXR5IHRoYXQgb3VyIGtub3duIGN1bHR1cmFsIHBoeWxvZ2VueSBjYW1lIFxuIGZyb20gZWFjaCB0eXBlIG9mIHNpbXVsYXRlZCBtZWNoYW5pc20iLCB4bGFiPSJudW1iZXIgb2Ygc2ltdWxhdGlvbiByZXBsaWNhdGVzIiwgY2V4LmF4aXM9MS41LCBjZXgubGFiPTEuNSkKCgpiYXNpY19tZWFuIDwtIGFnZ3JlZ2F0ZSh4ID0gcmVzX2FjdW1bLCAyMV0sIGJ5ID0gbGlzdCgocmVzX2FjdW1bLCAxXSkpLCBGVU4gPSBtZWFuKQpkaWZmdXNpb25fbWVhbiA8LSBhZ2dyZWdhdGUoeCA9IHJlc19hY3VtWywgMjJdLCBieSA9IGxpc3QoKHJlc19hY3VtWywgMV0pKSwgRlVOID0gbWVhbikKVE9fbWVhbiA8LSBhZ2dyZWdhdGUoeCA9IHJlc19hY3VtWywgMjNdLCBieSA9IGxpc3QoKHJlc19hY3VtWywgMV0pKSwgRlVOID0gbWVhbikKYm90aF9tZWFuIDwtIGFnZ3JlZ2F0ZSh4ID0gcmVzX2FjdW1bLCAyNF0sIGJ5ID0gbGlzdCgocmVzX2FjdW1bLCAxXSkpLCBGVU4gPSBtZWFuKQogICAgICAgICAgICAgICAgICAgCmJhc2ljX3NkIDwtIGFnZ3JlZ2F0ZSh4ID0gcmVzX2FjdW1bLCAyMV0sIGJ5ID0gbGlzdCgocmVzX2FjdW1bLCAxXSkpLCBGVU4gPSBzZCkKZGlmZnVzaW9uX3NkIDwtIGFnZ3JlZ2F0ZSh4ID0gcmVzX2FjdW1bLCAyMl0sIGJ5ID0gbGlzdCgocmVzX2FjdW1bLCAxXSkpLCBGVU4gPSBzZCkKVE9fc2QgPC0gYWdncmVnYXRlKHggPSByZXNfYWN1bVssIDIzXSwgYnkgPSBsaXN0KChyZXNfYWN1bVssIDFdKSksIEZVTiA9IHNkKQpib3RoX3NkIDwtIGFnZ3JlZ2F0ZSh4ID0gcmVzX2FjdW1bLCAyNF0sIGJ5ID0gbGlzdCgocmVzX2FjdW1bLCAxXSkpLCBGVU4gPSBzZCkKCgpwb2x5Z29uKHg9YyhiYXNpY19tZWFuWywxXSwgcmV2KGJhc2ljX21lYW5bLDFdKSksIHk9YyhiYXNpY19tZWFuWywyXSArIGJhc2ljX3NkWywyXSwgcmV2KGJhc2ljX21lYW5bLDJdIC0gYmFzaWNfc2RbLDJdKSksIGNvbD1hZGp1c3Rjb2xvcigib3JhbmdlIiwgYWxwaGE9MC41KSwgYm9yZGVyPU5BKQpwb2x5Z29uKHg9YyhkaWZmdXNpb25fbWVhblssMV0sIHJldihkaWZmdXNpb25fbWVhblssMV0pKSwgeT1jKGRpZmZ1c2lvbl9tZWFuWywyXSArIGRpZmZ1c2lvbl9zZFssMl0sIHJldihkaWZmdXNpb25fbWVhblssMl0gLSBkaWZmdXNpb25fc2RbLDJdKSksIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0wLjUpLCBib3JkZXI9TkEpCnBvbHlnb24oeD1jKFRPX21lYW5bLDFdLCByZXYoVE9fbWVhblssMV0pKSwgeT1jKFRPX21lYW5bLDJdICsgVE9fc2RbLDJdLCByZXYoVE9fbWVhblssMl0gLSBUT19zZFssMl0pKSwgY29sPWFkanVzdGNvbG9yKCJwaW5rIiwgYWxwaGE9MC41KSwgYm9yZGVyPU5BKQpwb2x5Z29uKHg9Yyhib3RoX21lYW5bLDFdLCByZXYoYm90aF9tZWFuWywxXSkpLCB5PWMoYm90aF9tZWFuWywyXSArIGJvdGhfc2RbLDJdLCByZXYoYm90aF9tZWFuWywyXSAtIGJvdGhfc2RbLDJdKSksIGNvbD1hZGp1c3Rjb2xvcigiZGFya2dyZWVuIiwgYWxwaGE9MC41KSwgYm9yZGVyPU5BKQoKCgpsaW5lcyhiYXNpY19tZWFuLCAgY29sPSJvcmFuZ2UiKQpsaW5lcyhkaWZmdXNpb25fbWVhbiwgIGNvbD0iY29ybmZsb3dlcmJsdWUiKQpsaW5lcyhUT19tZWFuLCAgY29sPSJwaW5rIikKbGluZXMoYm90aF9tZWFuLCAgY29sPSJkYXJrZ3JlZW4iKQoKbGFicyA8LSBjKCJCYXNpYyIsICIrRGlmZnVzaW9uIiwgIitUYWtlb3ZlciIsICIrRGlmZnVzaW9uICtUYWtlb3ZlciIpCmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQ9bGFicywgY29sPWMoIm9yYW5nZSIsICJjb3JuZmxvd2VyYmx1ZSIsICJwaW5rIiwgImRhcmtncmVlbiIpLCBsdHk9MSwgY2V4PTEuNSwgbHdkPTEwKQoKZGV2Lm9mZigpCmBgYAohW10ocHJlZGljdGlvbnMucG5nKQoKCgpgYGB7ciBldmFsPUZBTFNFfQoKcG5nKCJsYXN0X3N0ZXBfcHJlZGljdGlvbnMucG5nIiwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gOC41LCByZXMgPSAzMDAsIHVuaXRzID0gImluIikKcGFyKG1hcj1jKDUsNiwyLDEpKQp0aGlzIDwtIGxlbmd0aChiYXNpY19tZWFuWywxXSkKCnBsb3QoeD1jKDAsNSksIHk9YygwLDEpLCB0eXBlPSJuIiwgIHhheHQ9Im4iLCB5bGFiPSJwcm9iYWJpbGl0eSB0aGF0IG91ciBrbm93biBjdWx0dXJhbCBwaHlsb2dlbnkgY2FtZSBcbiBmcm9tIGVhY2ggdHlwZSBvZiBzaW11bGF0ZWQgbWVjaGFuaXNtIiwgeGxhYj0iIiwgeGxpbT1jKDAuNSw0LjUpKQpwb2x5Z29uKHg9YygwLjc1LDAuNzUsMS4yNSwxLjI1KSwgeT1jKGJhc2ljX21lYW5bdGhpcywyXSAtIGJhc2ljX3NkW3RoaXMsMl0sIGJhc2ljX21lYW5bdGhpcywyXSArIGJhc2ljX3NkW3RoaXMsMl0sIGJhc2ljX21lYW5bdGhpcywyXSArIGJhc2ljX3NkW3RoaXMsMl0sIGJhc2ljX21lYW5bdGhpcywyXSAtIGJhc2ljX3NkW3RoaXMsMl0pLCBjb2wgPSBhZGp1c3Rjb2xvcigib3JhbmdlIiwgYWxwaGE9MC41KSwgYm9yZGVyPU5BICkKcG9seWdvbih4PWMoMS43NSwxLjc1LDIuMjUsMi4yNSksIHk9YyhkaWZmdXNpb25fbWVhblt0aGlzLDJdIC0gZGlmZnVzaW9uX3NkW3RoaXMsMl0sIGRpZmZ1c2lvbl9tZWFuW3RoaXMsMl0gKyBkaWZmdXNpb25fc2RbdGhpcywyXSwgZGlmZnVzaW9uX21lYW5bdGhpcywyXSArIGRpZmZ1c2lvbl9zZFt0aGlzLDJdLCBkaWZmdXNpb25fbWVhblt0aGlzLDJdIC0gZGlmZnVzaW9uX3NkW3RoaXMsMl0pLCBjb2wgPSBhZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0wLjUpLCBib3JkZXI9TkEpCnBvbHlnb24oeD1jKDIuNzUsMi43NSwzLjI1LDMuMjUpLCB5PWMoVE9fbWVhblt0aGlzLDJdIC0gVE9fc2RbdGhpcywyXSwgVE9fbWVhblt0aGlzLDJdICsgVE9fc2RbdGhpcywyXSwgVE9fbWVhblt0aGlzLDJdICsgVE9fc2RbdGhpcywyXSwgVE9fbWVhblt0aGlzLDJdIC0gVE9fc2RbdGhpcywyXSksIGNvbCA9IGFkanVzdGNvbG9yKCJwaW5rIiwgYWxwaGE9MC41KSwgYm9yZGVyPU5BKQpwb2x5Z29uKHg9YygzLjc1LDMuNzUsNC4yNSw0LjI1KSwgeT1jKGJvdGhfbWVhblt0aGlzLDJdIC0gYm90aF9zZFt0aGlzLDJdLCBib3RoX21lYW5bdGhpcywyXSArIGJvdGhfc2RbdGhpcywyXSwgYm90aF9tZWFuW3RoaXMsMl0gKyBib3RoX3NkW3RoaXMsMl0sIGJvdGhfbWVhblt0aGlzLDJdIC0gYm90aF9zZFt0aGlzLDJdKSwgY29sID0gYWRqdXN0Y29sb3IoImRhcmtncmVlbiIsIGFscGhhPTAuNSksIGJvcmRlcj1OQSkKCgpsaW5lcyh4PWMoMC41LCAxLjUpLCBjKGJhc2ljX21lYW5bdGhpcywyXSwgYmFzaWNfbWVhblt0aGlzLDJdKSwgIGNvbD0ib3JhbmdlIikKbGluZXMoeD1jKDEuNSwgMi41KSwgYyhkaWZmdXNpb25fbWVhblt0aGlzLDJdLCBkaWZmdXNpb25fbWVhblt0aGlzLDJdKSwgIGNvbD0iY29ybmZsb3dlcmJsdWUiKQpsaW5lcyh4PWMoMi41LCAzLjUpLCBjKFRPX21lYW5bdGhpcywyXSwgVE9fbWVhblt0aGlzLDJdKSwgIGNvbD0icGluayIpCmxpbmVzKHg9YygzLjUsIDQuNSksIGMoYm90aF9tZWFuW3RoaXMsMl0sIGJvdGhfbWVhblt0aGlzLDJdKSwgIGNvbD0iZGFya2dyZWVuIikKCmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQ9bGFicywgY29sPWMoIm9yYW5nZSIsICJjb3JuZmxvd2VyYmx1ZSIsICJwaW5rIiwgImRhcmtncmVlbiIpLCBsdHk9MSwgY2V4PTEuNSwgbHdkPTEwKQoKZGV2Lm9mZigpCmBgYAohW10obGFzdF9zdGVwX3ByZWRpY3Rpb25zLnBuZykKCgpgYGB7ciwgZXZhbD1GQUxTRX0KI2NvbG5hbWVzKHJlc19hY3VtKQoKbG9uZyA8LSBsZW5ndGgoQ29uZnVzaW9uXzAxMDFfbWVhblssMV0pCkNvbmZ1c2lvbl9zdW1fMDEgPC0gYWdncmVnYXRlKHggPSByZXNfYWN1bVssIDU6OF0sIGJ5ID0gbGlzdCgocmVzX2FjdW1bLCAxXSkpLCBGVU4gPSBtZWFuKQpDb25mdXNpb25fc3VtXzAyIDwtIGFnZ3JlZ2F0ZSh4ID0gcmVzX2FjdW1bLCA5OjEyXSwgYnkgPSBsaXN0KChyZXNfYWN1bVssIDFdKSksIEZVTiA9IG1lYW4pCkNvbmZ1c2lvbl9zdW1fMDMgPC0gYWdncmVnYXRlKHggPSByZXNfYWN1bVssIDEzOjE2XSwgYnkgPSBsaXN0KChyZXNfYWN1bVssIDFdKSksIEZVTiA9IG1lYW4pCkNvbmZ1c2lvbl9zdW1fMDQgPC0gYWdncmVnYXRlKHggPSByZXNfYWN1bVssIDE3OjIwXSwgYnkgPSBsaXN0KChyZXNfYWN1bVssIDFdKSksIEZVTiA9IG1lYW4pCgpwZXJjZW50X29uZSA8LSAoQ29uZnVzaW9uX3N1bV8wMVssMjo1XS9zdW0oQ29uZnVzaW9uX3N1bV8wMVssMjo1XSkpICogMTAwCnBlcmNlbnRfdHdvIDwtIChDb25mdXNpb25fc3VtXzAyWywyOjVdL3N1bShDb25mdXNpb25fc3VtXzAyWywyOjVdKSkgKiAxMDAKcGVyY2VudF90aHJlZSA8LSAoQ29uZnVzaW9uX3N1bV8wM1ssMjo1XS9zdW0oQ29uZnVzaW9uX3N1bV8wM1ssMjo1XSkpICogMTAwCnBlcmNlbnRfZm91ciA8LSAoQ29uZnVzaW9uX3N1bV8wNFssMjo1XS9zdW0oQ29uZnVzaW9uX3N1bV8wNFssMjo1XSkpICogMTAwCgoKY29uZnVzaW9uIDwtIG1hdHJpeChjKHBlcmNlbnRfb25lLCBwZXJjZW50X3R3bywgcGVyY2VudF90aHJlZSwgcGVyY2VudF9mb3VyKSwgNCw0KQoKbGFicyA8LSBjKCJCYXNpYyIsICIrRGlmZnVzaW9uIiwgIitUYWtlb3ZlciIsICIrRGlmZnVzaW9uIiwgICIrVGFrZW92ZXIiKQpjb2xvcnMxIDwtIGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiZ3JleTk1IiwgImdyZXk0MCIpKQoKCnBuZygiY29uZnVzaW9uX21hdHJpeC5wbmciLCB3aWR0aCA9IDguNSwgaGVpZ2h0ID0gOC41LCByZXMgPSA2MDAsIHVuaXRzID0gImluIikKcGFyKG1hcj1jKDgsOCwxLDEpKQpwbG90KDAsMCx4bGltPWMoLTAuMiwxLjQpLCB5bGltPWMoLTAuMiwxLjQpLCB4YXh0PSJuIiwgeGxhYj0iIiwgeWF4dD0ibiIsIHlsYWI9IiIgLCBidHk9Im4iKQojaW1hZ2UocHJvcCwgY29sID0gY29sb3JzMSgyMCksIGF4ZXM9RkFMU0UpCmF4aXMoMSwgYXQ9YygwLCAuNCwgLjgsIDEuMiwgMS4zKSwgbGFiZWxzPWxhYnMsIHRpY2sgPSBGQUxTRSwgbGluZSA9IEZBTFNFLCBjZXguYXhpcyA9IDEsIHBvcyA9IC0uMTksIGxhcz0yKQpheGlzKDIsIGF0PXJldihjKC0wLjA1LCAwLjA1LCAuNCwgLjgsIDEuMikpLCBsYWJlbHM9bGFicywgdGljayA9IEZBTFNFLCBsaW5lID0gRkFMU0UsIGNleC5heGlzID0gMSwgbGFzPTIpCm10ZXh0KCJwZXJjZW50IG9mIHRpbWUgdGhhdCBSRiBpZGVudGlmaWVzIGlucHV0IG1vZGVsIGFzIGVhY2ggbW9kZWwgdHlwZSIsIHNpZGUgPSAxLCBwYWRqID0gMTAsIGNleCA9IDEpCm10ZXh0KCJrbm93biBtb2RlbCB0eXBlIGdpdmVuIHRvIHJhbmRvbSBmb3Jlc3QiLCBzaWRlID0gMiwgcGFkaiA9IC0xMCwgY2V4ID0gMSkKCgoKZm9yKGkgaW4gMTo0KSB7CiAgZm9yKGogaW4gNDoxKSB7CiAgICB4cyA8LSBjKDAsIC40LCAuOCwgMS4yKVtpXQogICAgeXMgPC0gcmV2KGMoMCwgLjQsIC44LCAxLjIpKVtqXQogICAgcG9seWdvbih4PWMoeHMtMC4yLCB4cy0wLjIsIHhzKzAuMiwgeHMrMC4yKSwgeT1jKHlzLTAuMiwgeXMrMC4yLCB5cyswLjIsIHlzLTAuMiksIGNvbD1jb2xvcnMxKDEwMClbcm91bmQoYXMubnVtZXJpYyhjb25mdXNpb25baSwgal0pLCAxKSsxXSkKICAgIGlmKGkgPT0gail7dGV4dCh4ID0geHMsIHkgPSB5cywgcGFzdGUwKHJvdW5kKGFzLm51bWVyaWMoY29uZnVzaW9uW2ksIGpdKSwgMiksICIlIiksIGNleCA9IDIuMiwgY29sPSJsaW1lZ3JlZW4iKX1lbHNleyh0ZXh0KHggPSB4cywgeSA9IHlzLCBwYXN0ZTAocm91bmQoYXMubnVtZXJpYyhjb25mdXNpb25baSwgal0pLCAyKSwgIiUiKSwgY2V4ID0gMi4yLCBjb2w9ImJsYWNrIikpfQogIH0KfQoKYGBgCgoKIVtdKGNvbmZ1c2lvbl9tYXRyaXgucG5nKQoKCgoKCmBgYHtyLCBldmFsPUZBTFNFfQoKCgoKIyBWYXJpYWJsZXMgaW1wb3J0YW5jZQoKaW1wIDwtIGltcG9ydGFuY2UoZml0KQppbXAgPC0gYXBwbHkoaW1wLCAyLCBmdW5jdGlvbih4KSAoeCAtIG1pbih4KSkvKG1heCh4KSAtIG1pbih4KSkpCmltcCA8LSBpbXBbc29ydChpbXBbLCA1XSwgaW5kZXgucmV0dXJuID0gVFJVRSwgZGVjcmVhc2luZyA9IFRSVUUpJGl4LCBdCgphcy5jaGFyYWN0ZXIocmVwbGFjZVssIDZdKQpsb2FkKGFzLmNoYXJhY3RlcihyZXBsYWNlWzkwLCA2XSkpCmltcCA8LSBpbXBvcnRhbmNlKGZpdCkKCm5hbWVzIDwtIHJvd25hbWVzKGltcCkKbmFtZXNbbmFtZXMgPT0gInNwYXRpYWwudGVzdHMuZm9yYSJdIDwtICJTcGFjZSBGIgpuYW1lc1tuYW1lcyA9PSAic3BhdGlhbC50ZXN0cy5kb20iXSA8LSAiU3BhY2UgRCIKbmFtZXNbbmFtZXMgPT0gInNwcmF0ZSJdIDwtICJTcChyYXRpbykiCm5hbWVzW25hbWVzID09ICJ0cmFuc2l0aW9uX2Zyb21fdHJhaXRfMV90b18yIl0gPC0gIlRSKDEtMikiCm5hbWVzW25hbWVzID09ICJ0cmFuc2l0aW9uX2Zyb21fdHJhaXRfMl90b18xIl0gPC0gIlRSKDItMSkiCm5hbWVzW25hbWVzID09ICJQaHlsb2dlbmV0aWNfc2lnbmFsIl0gPC0gIlBoeVNpZyhEKSIKbmFtZXNbbmFtZXMgPT0gIkV2b2x1dGlvbmFyeV9kaXN0aW5jdGl2ZW5lc3Nfc3VtIl0gPC0gIkVEc3VtIgpuYW1lc1tuYW1lcyA9PSAiUHlsb19kaXZlcnNpdHlfaXNfc3VtX29mX0JMIl0gPC0gIlBEc3VtIgpuYW1lc1tuYW1lcyA9PSAidHJhbnNpdGlvbl9yYXRlX3JhdGlvXzF0bzJfb3Zlcl8ydG8xIl0gPC0gIlRSKHJhdGlvKSIKbmFtZXNbbmFtZXMgPT0gImdhbW1hIl0gPC0gIkdhbW1hIgpuYW1lc1tuYW1lcyA9PSAibWVhbl9QaHlsb2dlbmV0aWNfaXNvbGF0aW9uIl0gPC0gIk1QSSIKbmFtZXNbbmFtZXMgPT0gImV4dHJhdGUiXSA8LSAiRXh0KHJhdGlvKSIKbmFtZXNbbmFtZXMgPT0gImF2ZXJhZ2VfcGh5bG9nZW5ldGljX2RpdmVyc2l0eV9pc19tZWFuX29mX0JMIl0gPC0gIlBEbWVhbiIKbmFtZXNbbmFtZXMgPT0gImV4dGluY3Rpb25fcGVyX3NwZWNpYXRpb24iXSA8LSAiRFIiCm5hbWVzW25hbWVzID09ICJ2YXJpYW5jZV9QaHlsb2dlbmV0aWNfaXNvbGF0aW9uIl0gPC0gIlZQSSIKbmFtZXNbbmFtZXMgPT0gIkZfcXVhZHJhdGljX2VudHJvcHlfaXNfc3VtX29mX1BEIl0gPC0gIkYiCm5hbWVzW25hbWVzID09ICJNZWFuX3BhaXJ3aXNlX2Rpc3RhbmNlIl0gPC0gIk1QRCIKbmFtZXNbbmFtZXMgPT0gInZhcmlhbmNlX1B5bG9fZGl2ZXJzaXR5X2lzX3ZhcmlhbmNlX29mX0JMIl0gPC0gIlBEdmFyIgpuYW1lc1tuYW1lcyA9PSAidmFyaWFuY2VfcGFpcndpc2VfZGlzdGFuY2UiXSA8LSAiVlBEIgoKCnhfbGVuZ3RoIDwtIGxlbmd0aChsZXZlbHMocmVwbGFjZVssMV0pKQoKIyMjIyMjCiMgbWFrZSBtYXRyaXgKCgoKCgoKCgojIyMjIwojcGxvdCBmcm9tIG1hdHJpeAoKCgoKCgoKCgoKcGRmKGZpbGU9ImhlYXQgbWFwIG9mIHZhcmlhYmxlIGltcG9ydGFuY2UucGRmIiwgd2lkdGg9MTEsIGhlaWdodD04LjUpCgpwYXIobWFyPWMoNCwxMCw0LDEpKQpwbG90KHggPSBzZXEoMSwgeF9sZW5ndGgsIGJ5PTEpLCB5ID0gcmVwKDAsIHhfbGVuZ3RoKSwgdHlwZT0ibiIsIHlsaW09YygwLDIwKSwgeWxhYj0iIiwgeWF4dD0ibiIsIHhheHQ9Im4iLCB4bGFiPSIiLCB4bGltPWMoMSwgeF9sZW5ndGgqMTAwKSkKCmF4aXMoMiwgbGFiZWw9bmFtZXMsIGF0PXNlcSgwLjUsbGVuZ3RoKG5hbWVzKS0uNSksIGxhcz0yKQoKCmZvcihpIGluIDE6bGVuZ3RoKGFzLmNoYXJhY3RlcihyZXBsYWNlWywgNl0pKSl7CgkKCWxvYWQoYXMuY2hhcmFjdGVyKHJlcGxhY2VbaSwgNl0pKQoJaW1wIDwtIGltcG9ydGFuY2UoZml0KQojaW1wIDwtIGFwcGx5KGltcCwgMiwgZnVuY3Rpb24oeCkgKHggLSBtaW4oeCkpLyhtYXgoeCkgLSBtaW4oeCkpKQoJI2kgPC0gMTAKCQp4X3JhbmdlIDwtIGMoaSwgaSwgaSsxLCBpKzEpCQpwZXJjZW50IDwtIGFzLm51bWVyaWMoY2VpbGluZyhpbXBbLDVdICkpCnBlcmNlbnRbd2hpY2gocGVyY2VudCA9PSAwKV0gPC0gTkEKY29sb3JzIDwtIGNtLmNvbG9ycygxMDApW3BlcmNlbnRdCgpmb3IoaiBpbiAwOjE5KXsKCXlfcmFuZ2UgPC0gYyhqLCBqKzEsIGorMSwgaikKcG9seWdvbih4ID0geF9yYW5nZSwgeSA9IHlfcmFuZ2UsIGNvbD0gY29sb3JzW2orMV0sIGJvcmRlcj1OQSkKfQoKfQoKYWJsaW5lKHY9c2VxKDAsMTAwMCwgYnk9MTAwKSkKCmF4aXMoMSwgbGFiZWw9Zm9ybWF0KHVuaXF1ZShyZXBsYWNlWyw3XSksIGZvcm1hdD0iJWQgJWIgJVkiKSwgYXQ9c2VxKDUwLDk1MCwgYnk9MTAwKVsxOmxlbmd0aCh1bmlxdWUocmVwbGFjZVssN10pKV0pCmRldi5vZmYoKQoKYGBgCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKYGBge3IgZXZhbD1GQUxTRX0KaW1wb3J0YW5jZShmaXQpCmBgYAoKCmBgYHtyIGV2YWw9RkFMU0V9CiMgVmFyaWFibGVzIGltcG9ydGFuY2UKCmltcCA8LSBpbXBvcnRhbmNlKGZpdCkKaW1wIDwtIGFwcGx5KGltcCwgMiwgZnVuY3Rpb24oeCkgKHggLSBtaW4oeCkpLyhtYXgoeCkgLSBtaW4oeCkpKQppbXAgPC0gaW1wW3NvcnQoaW1wWywgNV0sIGluZGV4LnJldHVybiA9IFRSVUUsIGRlY3JlYXNpbmcgPSBUUlVFKSRpeCwgXQoKCm5hbWVzIDwtIHJvd25hbWVzKGltcCkKbmFtZXNbbmFtZXMgPT0gInNwYXRpYWwudGVzdHMuZm9yYSJdIDwtICJTcGFjZSBGIgpuYW1lc1tuYW1lcyA9PSAic3BhdGlhbC50ZXN0cy5kb20iXSA8LSAiU3BhY2UgRCIKbmFtZXNbbmFtZXMgPT0gInNwcmF0ZSJdIDwtICJTcChyYXRpbykiCm5hbWVzW25hbWVzID09ICJ0cmFuc2l0aW9uX2Zyb21fdHJhaXRfMV90b18yIl0gPC0gIlRSKDEtMikiCm5hbWVzW25hbWVzID09ICJ0cmFuc2l0aW9uX2Zyb21fdHJhaXRfMl90b18xIl0gPC0gIlRSKDItMSkiCm5hbWVzW25hbWVzID09ICJQaHlsb2dlbmV0aWNfc2lnbmFsIl0gPC0gIlBoeVNpZyhEKSIKbmFtZXNbbmFtZXMgPT0gIkV2b2x1dGlvbmFyeV9kaXN0aW5jdGl2ZW5lc3Nfc3VtIl0gPC0gIkVEc3VtIgpuYW1lc1tuYW1lcyA9PSAiUHlsb19kaXZlcnNpdHlfaXNfc3VtX29mX0JMIl0gPC0gIlBEc3VtIgpuYW1lc1tuYW1lcyA9PSAidHJhbnNpdGlvbl9yYXRlX3JhdGlvXzF0bzJfb3Zlcl8ydG8xIl0gPC0gIlRSKHJhdGlvKSIKbmFtZXNbbmFtZXMgPT0gImdhbW1hIl0gPC0gIkdhbW1hIgpuYW1lc1tuYW1lcyA9PSAibWVhbl9QaHlsb2dlbmV0aWNfaXNvbGF0aW9uIl0gPC0gIk1QSSIKbmFtZXNbbmFtZXMgPT0gImV4dHJhdGUiXSA8LSAiRXh0KHJhdGlvKSIKbmFtZXNbbmFtZXMgPT0gImF2ZXJhZ2VfcGh5bG9nZW5ldGljX2RpdmVyc2l0eV9pc19tZWFuX29mX0JMIl0gPC0gIlBEbWVhbiIKbmFtZXNbbmFtZXMgPT0gImV4dGluY3Rpb25fcGVyX3NwZWNpYXRpb24iXSA8LSAiRFIiCm5hbWVzW25hbWVzID09ICJ2YXJpYW5jZV9QaHlsb2dlbmV0aWNfaXNvbGF0aW9uIl0gPC0gIlZQSSIKbmFtZXNbbmFtZXMgPT0gIkZfcXVhZHJhdGljX2VudHJvcHlfaXNfc3VtX29mX1BEIl0gPC0gIkYiCm5hbWVzW25hbWVzID09ICJNZWFuX3BhaXJ3aXNlX2Rpc3RhbmNlIl0gPC0gIk1QRCIKbmFtZXNbbmFtZXMgPT0gInZhcmlhbmNlX1B5bG9fZGl2ZXJzaXR5X2lzX3ZhcmlhbmNlX29mX0JMIl0gPC0gIlBEdmFyIgpuYW1lc1tuYW1lcyA9PSAidmFyaWFuY2VfcGFpcndpc2VfZGlzdGFuY2UiXSA8LSAiVlBEIgoKCnBuZygidmFyX2ltcG9ydF9hbGwucG5nIiwgd2lkdGggPSAyNSwgaGVpZ2h0ID0gMjUsIHVuaXQ9ImluIiwgcmVzPTMwMCkKcGFyKG1hciA9IGMoMTAsIDE4LCAxLCAxKSkKcGxvdCh4ID0gcmV2KGltcFssIDVdKSwgeSA9IDE6bnJvdyhpbXApLCB0eXBlID0gImwiLCB5YXh0ID0gIm4iLCAKICAgICB5bGFiID0gIiIsIHhsYWIgPSAiVmFyaWFibGUgSW1wb3J0YW5jZSIsCiAgICAgeGxpbSA9IGMoMCwgMSksIGx3ZCA9IDIsIGNleC5sYWIgPSA0KQpmb3IgKGkgaW4gMTpucm93KGltcCkpIHsKICBhYmxpbmUoaCA9IGksIGx0eSA9IDMsIGNvbCA9ICJncmF5ODAiKQp9CmFibGluZSh2ID0gc2VxKDAsIDEsIDEvMTkpLCBsdHkgPSAzLCBjb2wgPSAiZ3JheTgwIikKCmxpbmVzKHggPSByZXYoaW1wWywgNF0pLCB5ID0gMTpucm93KGltcCksIGNvbCA9ICJkYXJrZ3JlZW4iLCBsd2QgPSAyKQpsaW5lcyh4ID0gcmV2KGltcFssIDNdKSwgeSA9IDE6bnJvdyhpbXApLCBjb2wgPSAicmVkIiwgbHdkID0gMikKbGluZXMoeCA9IHJldihpbXBbLCAyXSksIHkgPSAxOm5yb3coaW1wKSwgY29sID0gImJsdWUiLCBsd2QgPSAyKQpsaW5lcyh4ID0gcmV2KGltcFssIDFdKSwgeSA9IDE6bnJvdyhpbXApLCBjb2wgPSAiZGFya29yYW5nZTEiLCBsd2QgPSAyKQpsaW5lcyh4ID0gcmV2KGltcFssIDVdKSwgeSA9IDE6bnJvdyhpbXApLCBsd2QgPSAzKQoKcG9pbnRzKHggPSByZXYoaW1wWywgNF0pLCB5ID0gMTpucm93KGltcCksIGNvbCA9ICJkYXJrZ3JlZW4iLCBjZXggPSAyKQpwb2ludHMoeCA9IHJldihpbXBbLCAzXSksIHkgPSAxOm5yb3coaW1wKSwgY29sID0gInJlZCIsIGNleCA9IDIpCnBvaW50cyh4ID0gcmV2KGltcFssIDJdKSwgeSA9IDE6bnJvdyhpbXApLCBjb2wgPSAiYmx1ZSIsIGNleCA9IDIpCnBvaW50cyh4ID0gcmV2KGltcFssIDFdKSwgeSA9IDE6bnJvdyhpbXApLCBjb2wgPSAiZGFya29yYW5nZTEiLCBjZXggPSAyKQpwb2ludHMoeCA9IHJldihpbXBbLCA1XSksIHkgPSAxOm5yb3coaW1wKSwgcGNoID0gMjAsIGNleCA9IDMpCgoKdGV4dCh5ID0gMTpucm93KGltcCksIHggPSBwYXIoInVzciIpWzFdIC0gLjE3LCBsYWJlbHMgPSByZXYobmFtZXMpLAogICAgIHNydCA9IDAsIHBvcyA9IDQsIHhwZCA9IFQsIGNleCA9IDQpCmRldi5vZmYoKQpgYGAKCiFbXSh2YXJfaW1wb3J0X2FsbC5wbmcpCgoKCgpgYGB7ciBldmFsPUZBTFNFfQpwYXIobWZyb3c9YygyLDMpKQoKIyBCb3ggcGxvdHMKYm94cGxvdChzcGF0aWFsLnRlc3RzLmZvcmEgfiBNb2RlbCwgZGF0YSA9IGRhdGEuYW5hbHlzaXMuY29tcDMpCmFibGluZShoID0gYSRzcGF0aWFsLnRlc3RzLmZvcmEsIGNvbCA9ICJyZWQiLCBsdHkgPSAyKQoKYm94cGxvdChzcGF0aWFsLnRlc3RzLmRvbSB+IE1vZGVsLCBkYXRhID0gZGF0YS5hbmFseXNpcy5jb21wMykKYWJsaW5lKGggPSBhJHNwYXRpYWwudGVzdHMuZm9yYSwgY29sID0gInJlZCIsIGx0eSA9IDIpCgpib3hwbG90KGxvZyhzcHJhdGUpIH4gTW9kZWwsIGRhdGEgPSBkYXRhLmFuYWx5c2lzLmNvbXAzLCB5bGltID0gYygtMTAsIDEwKSkKYWJsaW5lKGggPSBsb2coYSRzcHJhdGUpLCBjb2wgPSAicmVkIiwgbHR5ID0gMikKCmJveHBsb3QobG9nKGV4dHJhdGUpIH4gTW9kZWwsIGRhdGEgPSBkYXRhLmFuYWx5c2lzLmNvbXAzLCB5bGltID0gYygtMTAsIDEwKSkKYWJsaW5lKGggPSBsb2coYSRleHRyYXRlKSwgY29sID0gInJlZCIsIGx0eSA9IDIpCgpib3hwbG90KGxvZyh0cmFuc2l0aW9uX3JhdGVfcmF0aW9fMXRvMl9vdmVyXzJ0bzEpIH4gTW9kZWwsIGRhdGEgPSBkYXRhLmFuYWx5c2lzLmNvbXAzKQphYmxpbmUoaCA9IGxvZyhhJHNwcmF0ZSksIGNvbCA9ICJyZWQiLCBsdHkgPSAyKQoKYm94cGxvdChQaHlsb2dlbmV0aWNfc2lnbmFsIH4gTW9kZWwsIGRhdGEgPSBkYXRhLmFuYWx5c2lzLmNvbXAzLCB5bGltID0gYygwLCAxKSkKYWJsaW5lKGggPSBhJFBoeWxvZ2VuZXRpY19zaWduYWwsIGNvbCA9ICJyZWQiLCBsdHkgPSAyKQoKCmBgYAoKCgpgYGB7ciBldmFsPUZBTFNFfQojYnVpbGQgYSBkYXRhIHRyYWNraW5nIHRhYmxlIHRvIHRyYWNrIHBhcmFtZXRlciBjaGFuZ2VzIHRocm91Z2ggdGltZQoKc3RyKGZpdCkKCnkKCmBgYAoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK